posts - 183,  comments - 10,  trackbacks - 0
 

二分查找的变形

传统的二分查找
数组时有序的,要么升序要么降序,这里不考虑重复元素出现的情况。

int foo(int a[], int n, int item)
{
    
int left = 0, right = n - 1;
    
int middle = 0;
    
while (left <= right)
    {
        middle 
= (left + right) / 2;
        
if (item > a[middle])
        {
            left 
= middle + 1;
        }
        
else if (item < a[middle])
        {
            right 
= middle - 1;
        }
        
else
        {
            
return middle;
        
        }
    }
    
return -1;
}

 


二分查找的变形
给定一个数组 int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
如果对该数组调整为
{6, 7, 8, 9, 10, 1, 2, 3, 4, 5}
调整后即是两个连续有序序列,只不过整体不有序
还是用二分策略,但是具体细节需要改进
整体结构还是一样的
具体补充的是当待查找元素与中间元素不相等时,
如果小于,还要检测 item 与 a[left] 的大小关系
如果大于,还要检测 item 与 a[right] 的大小关系

int bar(int a[], int n, int item)
{
    
int left = 0, right = n - 1;
    
int middle = 0;
    
while (left <= right)
    {
        middle 
= (left + right) / 2;
        
if (item < a[middle])
        {
            
if (a[left] < item)
            {
                right 
= middle - 1;
            }
            
else if (a[left] > item)
            {
                left 
= middle + 1;
            }
            
else
            {
                
return left;
            }
            
// right = middle - 1;
        }
        
else if (item > a[middle])
        {
            
if (a[right] > item)
            {
                left 
= middle + 1;
            }
            
else if (a[right] < item)
            {
                right 
= middle - 1;
            }
            
else
            {
                
return right;
            }
        }
        
else
        {
            
return middle;
        }
    }
    
return -1;
}

 1 #include <iostream>
 2 using namespace std;
 3 
 4 int foo(int a[], int n, int item)
 5 {
 6     int left = 0, right = n - 1;
 7     int middle = 0;
 8     while (left <= right)
 9     {
10         middle = (left + right) / 2;
11         if (item > a[middle])
12         {
13             left = middle + 1;
14         }
15         else if (item < a[middle])
16         {
17             right = middle - 1;
18         }
19         else
20         {
21             return middle;
22         
23         }
24     }
25     return -1;
26 }
27 
28 int bar(int a[], int n, int item)
29 {
30     int left = 0, right = n - 1;
31     int middle = 0;
32     while (left <= right)
33     {
34         middle = (left + right) / 2;
35         if (item < a[middle])
36         {
37             if (a[left] < item)
38             {
39                 right = middle - 1;
40             }
41             else if (a[left] > item)
42             {
43                 left = middle + 1;
44             }
45             else
46             {
47                 return left;
48             }
49             // right = middle - 1;
50         }
51         else if (item > a[middle])
52         {
53             if (a[right] > item)
54             {
55                 left = middle + 1;
56             }
57             else if (a[right] < item)
58             {
59                 right = middle - 1;
60             }
61             else
62             {
63                 return right;
64             }
65         }
66         else
67         {
68             return middle;
69         }
70     }
71     return -1;
72 }
73 
74 int main()
75 {
76     int a[] = {12345678910};
77     cout << foo(a, sizeof (a) / sizeof (*a), 3<< endl;
78     int b[] = {67891012345};
79     cout << bar(b, sizeof (b) / sizeof (*b), 5<< endl;
80     return 0;
81 }

实现:



posted @ 2011-10-29 00:04 unixfy 阅读(175) | 评论 (0)编辑 收藏

不同组的组合

有 N 个组,每个组中有不定个元素,从每个组中选择一个元素,例如:
第一组 1 2
第二组 3 4
第三组 5
结果为:
1 3 5
1 4 5
2 3 5
2 4 5

http://topic.csdn.net/u/20100313/23/51e49d61-8a36-47f5-8e3b-20477dafde55.html

 1 #include <iostream>
 2 #include <string>
 3 #include <vector>
 4 using namespace std;
 5 
 6 void foo(vector<vector<string> >& result, vector<string>& temp, const vector<vector<string> >& vvs, size_t m)
 7 {
 8     if (temp.size() >= vvs.size())
 9     {
10         result.push_back(temp);
11         for (size_t i = 0; i != temp.size(); ++i)
12         {
13             cout << temp[i] << ' ';
14         }
15         cout << endl;
16     }
17     else
18     {
19         for (size_t i = 0; i != vvs[m].size(); ++i)
20         {
21             temp.push_back(vvs[m][i]);
22             foo(result, temp, vvs, m + 1);
23             temp.pop_back();
24         }
25     }
26 }
27 
28 void bar(vector<vector<string> >& result, const vector<vector<string> >& vvs)
29 {
30     vector<string> temp;
31     foo(result, temp, vvs, 0);
32 }
33 
34 int main()
35 {
36     vector<vector<string> > vvs;
37     vector<string> vs;
38     vs.push_back("A1");
39     vs.push_back("A2");
40     vvs.push_back(vs);
41     vs.clear();
42     vs.push_back("B1");
43     vs.push_back("B2");
44     vvs.push_back(vs);
45     vs.clear();
46     vs.push_back("C1");
47     vs.push_back("C2");
48     vs.push_back("C3");
49     vvs.push_back(vs);
50     vs.clear();
51     for (size_t i = 0; i != vvs.size(); ++i)
52     {
53         for (size_t j = 0; j != vvs[i].size(); ++j)
54         {
55             cout << vvs[i][j] << ' ';
56         }
57         cout << endl;
58     }
59     cout << endl;
60     vector<vector<string> > result;
61     bar(result, vvs);
62     cout << endl;
63     for (size_t i = 0; i != result.size(); ++i)
64     {
65         for (size_t j = 0; j != result[i].size(); ++j)
66         {
67             cout << result[i][j] << ' ';
68         }
69         cout << endl;
70     }
71     return 0;
72 }


posted @ 2011-10-06 13:23 unixfy 阅读(165) | 评论 (0)编辑 收藏

从 n 个数种选出 m 个数,随机

思路来源于《编程珠玑》和 TAOCP
问题来源:http://topic.csdn.net/u/20110920/20/94c9eba8-ccdf-44eb-b9bc-f2707ca78c99.html
http://hi.baidu.com/unixfy/blog/item/f064063266f1cdc9a3cc2b81.html

解法:
for (int i = 0; i != n; ++i)
{
 if (rand() % (n - i) < m)
 {
  printf("%d ", a[i]);
  --m;
 }
}

重点在于该循环。
遍历整个 n 个元素的数组,随机生成一个数,这个数为 0 - (n - i) 之间,判断其是否小于 m
i 每次循环自加,所以说对于最大的数只有 m / n 几率被选中,如果前面 n - m 次都没有选中元素,那么在 n - m + 1 次就必须选中一个元素,几率是 100% 的,后面的也是 100%。
选中一个元素则 m 自减,对剩下的 n - i 个元素还有 m - j 个元素需要选择。每个元素被选中的概率是一样的即 m / n, 不被选中的概率也是一样的,即 (n - m) / n 。
一个循环 O(N) 的时间复杂度。

 1 #include <stdio.h>
 2 #include <time.h>
 3 #include <stdlib.h>
 4 
 5 void foo(int a[], int n, int m)
 6 {
 7     srand(time(0));
 8     for (int i = 0; i != n; ++i)
 9     {
10         if (rand() % (n - i) < m)
11         {
12             printf("%d ", a[i]);
13             --m;
14         }
15     }
16     printf("\n");
17 }
18 
19 int main()
20 {
21     int a[35];
22     for (int i = 0; i != 35++i)
23     {
24         a[i] = i;
25     }
26     foo(a, 358);
27     return 0;
28 }

 


posted @ 2011-09-22 23:48 unixfy 阅读(1492) | 评论 (1)编辑 收藏

排列与组合
问题描述:
对一个字符串,求出其所有的全排列情况和所有的组合情况。
例如字符串 abc
其所有的全排列是 abc, acb, bac, bca, cab, cba
其所有的组合是:a, b, c, ab, ac, bc, abc

全排列:
固定前面的元素,对后面的进行递归求解,求解后,恢复之前的状态,并将后面的元素与前面的进行交换。
代码描述:

void Permutation(char* pStr, char* pBegin);

void Permutation(char* pStr)
{
      Permutation(pStr, pStr);
}

void Permutation(char* pStr, char* pBegin)
{
      
if(!pStr || !pBegin)
            
return;
            
      
if(*pBegin == '\0')
      {
            printf(
"%s\n", pStr);
      }
      
else
      {
            
for(char* pCh = pBegin; *pCh != '\0'++ pCh)
            {
                  
char temp = *pCh;
                  
*pCh = *pBegin;
                  
*pBegin = temp;

                  Permutation(pStr, pBegin 
+ 1);

                  temp 
= *pCh;
                  
*pCh = *pBegin;
                  
*pBegin = temp;
            }
      }
}


组合:
对于组合,也是从头扫描字符串的第一个元素,按照数学上的公式,对于第一个元素有两种选择,一是取该元素,然后再剩下来的 n - 1 个元素中取 m - 1 个;而是不去该元素,然后在 n - 1 个元素中取 m 个元素。
C(m, n) = C(m - 1, n - 1) + C(m, n - 1)
这两种选择都可以进一步递归求解。
代码描述:

void Combination(char* string)
{
    
if(string == NULL)
        
return;

    
int length = strlen(string);
    vector
<char> result;
    
for(int i = 1; i <= length; ++ i)
    {
        Combination(
string, i, result);
    }
}

void Combination(char* stringint number, vector<char>& result)
{
    
if(number == 0)
    {
        vector
<char>::iterator iter = result.begin();
        
for(; iter < result.end(); ++ iter)
            printf(
"%c"*iter);
        printf(
"\n");
        
return;
    }
    
if(*string == '\0')
        
return;

    result.push_back(
*string);
    Combination(
string + 1, number - 1, result);
    result.pop_back();
    Combination(
string + 1, number, result);
}


参考:
字符串的排列
http://zhedahht.blog.163.com/blog/static/254111742007499363479/
字符串的组合
http://zhedahht.blog.163.com/blog/static/2541117420114172812217/

posted @ 2011-09-17 09:54 unixfy 阅读(111) | 评论 (0)编辑 收藏

程序员面试题精选

全部内容来自
浙大何海涛老师的博客 http://zhedahht.blog.163.com/
主要内容是数据结构、算法、C++、C#

程序员面试精选-何海涛-PDF
http://download.csdn.net/detail/goonyangxiaofang/3576580

posted @ 2011-09-14 13:15 unixfy 阅读(136) | 评论 (0)编辑 收藏

单链表的访问改进

我们知道单链表的插入和删除的时间复杂度是 O(1)
但是其访问的时间复杂度是 O(N),不能实现随机访问。

而顺序表是随机访问的,插入和删除的时间复杂度是 O(N)

针对单链表的访问弊端,如何改进单链表数据结构,使得访问的效率有所提升?

每种数据结构都有各自的优劣以及适用情况。

这里有几种方案,其实不能算在方案吧,而是采用其他数据结构替换的策略。

第一种方案
采用平衡二叉树,插入、删除、访问的复杂度都是 O(logN)
或者红黑树,插入、删除、访问的时间复杂度都是 O(logN)
STL 中的 set、map 可以完成该功能。

第二种方案
采用分段的策略
针对每个节点的值,根据值进行分段,段数视具体情况而定。
插入和删除的时间复杂度保持不变,还是 O(1)
访问的时间复杂度变为 O(N / 段的数目)
这种方式访问的时间复杂度得到一定的改进,但是是常数级的。
这种策略实质上是哈希。
哈希函数为除法函数。
例如有 0 1 2 3 4 5 6 7 8 9 十个数,可以分为两段,0 - 4 为第一段,5 - 9 为第二段。
访问一个数时,首先计算其所在的段,m / 5,得到所在段的首地址,然后去遍历访问。

第三种方案
采用线索二叉树
线索二叉树将二叉树线索化,二叉树可以想链表那样操作。插入和删除的时间复杂度都是 O(1)。
访问按照二叉树的方式,这时二叉树是平衡二叉树,访问的时间复杂度是 O(logN)。

几种方案的比较
                插入和删除   访问
单链表              O(1)    O(N)
平衡二叉树    O(logN)    O(logN)
分段                 O(1)    O(N / 段的数目)
线索二叉树         O(1)    O(logN)

总结
这几种方案,与其说是改进,不如说是更换另一种数据结构。

另外哈希方式,最好在存在大量数据的情况下使用,否则会浪费空间,因为哈希表很大。

针对单链表访问效率的改进,另一个角度是采用辅助性数据结构,记录一些信息,以方便快速地访问。

posted @ 2011-09-13 20:54 unixfy 阅读(737) | 评论 (0)编辑 收藏

最长重复子串

问题描述
给定一个字符串,求出其最长重复子串
例如 abcdabcd
最长重复子串是 abcd
最长重复子串可以重叠
例如
abcdabcda
这时最长重复子串是 abcda
中间的 a 是被重叠的。

直观的解法是,首先检测长度为 n - 1 的字符串情况,如果不存在重复则检测 n - 2, 一直递减下去,直到 1 。
这种方法的时间复杂度是 O(N * N * N),其中包括三部分,长度纬度、根据长度检测的字符串数目、字符串检测。

改进的方法是利用后缀数组
后缀数组是一种数据结构,对一个字符串生成相应的后缀数组后,然后再排序,排完序依次检测相邻的两个字符串的开头公共部分。
这样的时间复杂度为:
生成后缀数组 O(N)
排序 O(NlogN*N) 最后面的 N 是因为字符串比较也是 O(N)
依次检测相邻的两个字符串 O(N * N)
总的时间复杂度是 O(N^2*logN), 由于第一种方法的 O(N^3)

后缀数组的实现:
代码摘自 CSDN 论坛
http://topic.csdn.net/u/20071002/22/896b1597-fc39-466e-85d3-5bef6f7442f6.html

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 
 5 #define MAXCHAR 5000 //最长处理5000个字符
 6 
 7 char c[MAXCHAR], *a[MAXCHAR];
 8 
 9 int comlen( char *p, char *q ){
10     int i = 0;
11     while*&& (*p++ == *q++) )
12         ++i;
13     return i;
14 }
15 
16 int pstrcmp( const void *p1, const void *p2 ){
17     return strcmp( *(char* const *)p1, *(char* const*)p2 );
18 }
19 
20 int main(  ){
21     char ch;
22     int  n=0;
23     int  i, temp;
24     int  maxlen=0, maxi=0;
25     printf("Please input your string:\n");
26     while( (ch=getchar())!='\n' ){
27         a[n]=&c[n];
28         c[n++]=ch;
29     }
30     c[n]='\0';
31     qsort( a, n, sizeof(char*), pstrcmp );
32     for(i=0; i<n-1++i ){
33         temp=comlen( a[i], a[i+1] );
34         if( temp>maxlen ){
35             maxlen=temp;
36             maxi=i;
37         }
38     }
39     printf("%.*s\n",maxlen, a[maxi]);
40     system("PAUSE");
41     return 0;
42 }

 


参考:
http://topic.csdn.net/u/20071002/22/896b1597-fc39-466e-85d3-5bef6f7442f6.html
http://blog.csdn.net/kongming_acm/article/details/6232439
http://blog.sina.com.cn/s/blog_5133d4dd0100a4qd.html
http://www.programbbs.com/bbs/view35-20014-1.htm
http://hi.baidu.com/fangm/blog/item/58fd1a4c20a5eafdd72afcd0.html
http://www.cnblogs.com/dyh333/articles/1801714.html
http://www.byvoid.com/blog/tag/%E6%9C%80%E9%95%BF%E9%87%8D%E5%A4%8D%E5%AD%90%E4%B8%B2/
http://www.cppblog.com/Joe/archive/2011/08/19/153851.html
posted @ 2011-09-13 16:01 unixfy 阅读(7799) | 评论 (0)编辑 收藏

两个指针的作用

两个指针一般用在一个序列中。
在一个序列中处理问题时,如果只使用一个指针,可能会造成双重循环的问题,结果时间复杂度会是 O(N) 。
如果采用两个指针可以很好地解决问题,时间复杂度也可以得到改进。

采用两个指针的例子很多,这里举几个:
1.
自动文摘中,如果采用循环查找的方法,时间复杂度是幂次方。采用两个指针,分别指向文摘的开始处和结束处可以在 O(N) 的时间复杂度内找到文摘。

2.
求连续数字之和等于一给定数,例如给定数是 15 ,则结果有 1 2 3 4 5、4 5 6、7 8 三种结果。
如果采用循环的方法事件复杂度是 O(N^2)
可以采用两个指针,分别指向 small 和 big 。当 sum(small ... big) 大于给定数时,small 指针右移,当 sum 小于给定数时,big 指针右移。直到 small 是给定数的一半时。

3.
调整数组,是前半部分是某种类型的数,后半部分是某种类型的数。
比如前半部分是奇数,后半部分是偶数
前半部分是负数,后半部分是非负数
采用两个指针,分别从左右两端进行扫描,检测,如果符合条件则交换两数,直到两个指针交叉为止。

4.
求一个数组中两个数的和等于一定数。
先对数组排序
然后从数组两端用两个指针扫描,检测,直到两个指针交叉为止。

当一个指针无法很好解决问题时,应该再增添一个指针,多一个帮手。

posted @ 2011-09-13 13:12 unixfy 阅读(194) | 评论 (0)编辑 收藏

全排列

递归实现
·与第一个交换
·递归

*pch 与 *begin 之间的交换与复原
for (char* pch = pbegin; *pch != '\0'; ++pch)
{
 char temp = *pch;
 *pch = *pbegin;
 *pbegin = temp;

 permutation(pstr, pbegin + 1);

 temp = *pch;
 *pch = *pbegin;
 *pbegin = temp;
}
http://zhedahht.blog.163.com/blog/static/254111742007499363479/

 1 #include <iostream>
 2 using namespace std;
 3 
 4 void permutation(char* pstr, char* pbegin)
 5 {
 6     if (pstr == 0 || pbegin == 0)
 7     {
 8         return;
 9     }
10     if (*pbegin == '\0')
11     {
12         cout << pstr << endl;
13     }
14     else
15     {
16         for (char* pch = pbegin; *pch != '\0'++pch)
17         {
18             char temp = *pch;
19             *pch = *pbegin;
20             *pbegin = temp;
21 
22             permutation(pstr, pbegin + 1);
23 
24             temp = *pch;
25             *pch = *pbegin;
26             *pbegin = temp;
27         }
28     }
29 }
30 
31 void perm(char* str)
32 {
33     permutation(str, str);
34 }
35 
36 int main()
37 {
38     char s[4= "abc";
39     perm(s);
40 }

 


posted @ 2011-09-13 13:00 unixfy 阅读(84) | 评论 (0)编辑 收藏

二叉树的深度

二叉树的递归简历
二叉树的递归前序遍历
二叉树的递归深度求解

示例:
10
6
4
0
0
0
14
12
0
0
16
0
0
10 6 4 14 12 16
3

 1 #include <iostream>
 2 using namespace std;
 3 
 4 struct node
 5 {
 6     int value;
 7     node* left;
 8     node* right;
 9 };
10 
11 void create(node*& btree)
12 {
13     int n;
14     cin >> n;
15     if (n == 0)
16     {
17         btree = 0;
18     }
19     else
20     {
21         btree = new node;
22         btree->value = n;
23         create(btree->left);
24         create(btree->right);
25     }
26 }
27 
28 void preOrder(node* btree)
29 {
30     if (btree != 0)
31     {
32         cout << btree->value << ' ';
33         preOrder(btree->left);
34         preOrder(btree->right);
35     }
36 }
37 
38 int depth(node* btree)
39 {
40     if (btree == 0)
41     {
42         return 0;
43     }
44     else
45     {
46         int left = depth(btree->left);
47         int right = depth(btree->right);
48         return left > right ? left + 1 : right + 1;
49     }
50 }
51 
52 int main()
53 {
54     node* btree;
55     create(btree);
56     preOrder(btree);
57     cout << endl;
58     cout << depth(btree) << endl;
59 }

 


posted @ 2011-09-13 12:41 unixfy 阅读(160) | 评论 (0)编辑 收藏
仅列出标题
共19页: 1 2 3 4 5 6 7 8 9 Last