基本算法全集OK

1,排序算法


只要记得哪些排序是不稳定的就可以选对了,记得一句话 “学习真痛苦,“快”  “希”  “选” 一 “堆”  MM 来聊天吧。其中相应的分别是,快速排序、希尔排序、简单选择排序、堆排序,这几个都是不稳定排序,那其他的都是稳定排序了。


1.1 二分查找

int bSearch(int a[], int low, int high, int target){
	if(low > high)
		return -1;
	int mid = low +(high - low)/2;.//防止溢出
	if(target<a[mid])
		return bSearch(a,low,mid-1,target);
	else if(target>a[mid])
		return bSearch(a,mid+1,high,target);
	//if(target == a[mid])
	return mid;
}

http://www.cnblogs.com/ider/archive/2012/04/01/binary_search.html


1.2,选择排序

void SelectionSort(int a [], int n)
{
	int min;
	//循环n-1次
	for(int i = 0 ; i < n-1 ; i++)
	{
		min = i;
		for(int j = i+1; j < n; j++)
		{
			if(a[j]<a[min])
				min = j;
		}
		swap(a[i],a[min]);
	}
}

1.3,冒泡排序

void BubbleSort(int a[], int n)
{
	//循环n-1次
	for(int i=0; i<n-1; i++)
	{
		for(int j=0; j<n-i-1; j++)
		{
			if(a[j]>a[j+1])
			{
				swap(a[j],a[j+1]);
			}
		}
	}
}
void BubbleSort(int a[], int n)
{
	bool exchange;
	//循环n-1次
	for(int i=0; i<n-1; i++)
	{
		exchange = false;
		for(int j=0; j<n-i-1; j++)
		{
			if(a[j]>a[j+1])
			{
				swap(a[j],a[j+1]);
				exchange = true;
			}
		}
		if(!exchange)
			break;
	}
}

1.4,插入排序

void InsertSort(int a[], int n)
{
	//从第i=1个元素遍历到最后
	for(int i=1; i<n; i++)
	{
		//将第a[i]插入到前面已排序数组中
		int v = a[i];
		int j = i-1;
		while(j>0&&a[j]>v)
		{
			a[j+1] = a[j];
			j--;
		}
		a[j] = v;
	}
}

1.5,快速排序

int Partition(int a[], int low, int high)
{
	int v = a[low];
	int i = low;
	int j = high+1;
	while(true)
	{
		while(a[++i]<v);
		while(a[--j]>v);
		if(i<j)
		{
			swap(a[i],a[j]);
		}
		else//(i>=j)
		{
			break;
		}
	}
	swap(a[low],a[j]);
}

void QuickSort(int a[], int low, int high)
{
	if(low<high)
	{
		int pivot = Partition(a,low,high);
		QuickSort(a,low,pivot-1);
		QuickSort(a,pivot+1,high);
	}
}


快速排序的三路划分???


3,搜索算法

3.1 查找唯一重复元素

问题:数组中元素是否唯一

复杂:O(nlog(n))

解法:预排序O(nlog(n))+扫一遍查重O(n)


3.2 查找众数问题

问题:查找数组中出现最频繁元素。

复杂:O(nlog(n))

解法:预排序O(nlog(n))+扫一遍相同元素计数O(n)

3.3 查找过半元素问题

问题:数组中有一个数字出现的次数超过了数组总数的一半,找出这个数字。

出处:《编程之美》2.3 寻找发帖“水王”

复杂:O(n)

解法:

解法一:对所有ID进行排序,再扫描一遍排好序的ID列表,统计各个ID出现的次数,如果某个ID出现的次数超过总数的一半,那么就输出这个ID。时间复杂度为O(N*log2N + N).
解法二:既然已经排好序,而且有某个ID出现的次数超过总数的一半,那么ID列表的中间项一定是这个ID。复杂度为O(1)。
解法三:如果每次删除两个不同的ID(不管是否包含"水王"的ID),那么在剩下的ID列表中,"水王"的ID出现次数肯定仍然超过总数的一半。这样不断重复这个过程,总量就可以降低,从而避免了排序的操作,复杂度为O(N).

代码:

Type Find(Type* ID, int N)  
{  
     Type candidate;  
     int nTimes, i;  
     for(i = nTimes = 0; i < N; i++)  
     {  
          if(nTimes == 0)  
          {  
               candidate = ID[i], nTimes = 1;  
          }  
          else  
          {  
               if(candidate == ID[i])  
                    nTimes++;  
               else  
                    nTimes--;  
          }  
     }  
     return candidate;   
}  

问题扩展1:

数组中有一个数字出现的次数等于或大于了数组长度的一半,找出这个数字。

解答:据题意该数字出现次数一定超过1/3,因此可以从目标数组中去除三个不同的数,随后对剩余的数字进行如题目一的算法, 求出的结果就是我们的目标数字!


问题扩展2:

随着Tango的发展,管理员发现,“超级水王”没有了。统计结果表明,有3个发帖很多的ID,他们的发帖数目都超过了帖子总数目N的1/4。你能从发帖ID列表中快速找出他们的ID吗?

解答:参考上面的解法,思路如下:
如果每次删除四个不同的ID(不管是否包含发帖数目超过总数1/4的ID),那么,在剩下的ID列表中,原先发帖比例大于1/4的ID所占比例仍然大于1/4。可以通过不断重复这个过程,把ID列表中的ID总数降低(转化为更小的问题),从而得到问题的答案。

void Find(int *ID,int N,int candidate[3])
{
        int nTimes[3],i;
        nTimes[0]=0;
        nTimes[1]=0;
        nTimes[2]=0;
        candidate[0]=0;
        candidate[1]=0;
        candidate[2]=0;
        for(i=0;i<N;i++)
        {
                if(candidate[0]==ID[i])
                        nTimes[0]++;
                else if(candidate[1]==ID[i])
                        nTimes[1]++;
                else if(candidate[2]==ID[i])
                        nTimes[2]++;
                else if(nTimes[0]==0)
                {
                        candidate[0]=ID[i];
                        nTimes[0]=1;
                }
                else if(nTimes[1]==0)
                {
                        candidate[1]=ID[i];
                        nTimes[1]=1;
                }
                else if(nTimes[2]==0)
                {
                        candidate[2]=ID[i];
                        nTimes[2]=1;
                }
                else
                {
                        nTimes[0]--;
                        nTimes[1]--;
                        nTimes[2]--;
                }
        }
}

3.4 查找【最小|中值|最大|第k小|前k小】元素

问题:给定一个无序整数数组,返回这个数组中第k小的数。

出处:《算法设计与分析》P142计算中值和选择问题

复杂:O(n)

解法1:【排序+选择】最平常的思路是将数组排序,最快的排序是快排,然后返回已排序数组的第k个数,算法时间复杂度为O(nlogn+k)。

解法2:【改进的选择排序】使用选择排序思想,对这个乱序数组按照从大到小选择排序(只进行K次,避免做后N-K个数的排序),总的时间复杂度为O(n*k)。

解法3:【改进的快速排序】使用快速排序思想,但是每次只对patition之后的数组的一半递归,这样可以将时间复杂度为O(nlogk)近似为O(n)。(因为快排的partition操作复杂度O(n))

具体地,将数组按照第一个数字pivot进行划分,将比pivot小的放在左边,比pivot大的放在右边,pivot放中间。返回patition之后pivot的下标j。如果此时j==k(数组下标从1开始)那么说明a[j]就是要找的第k个数。如果j<k,递归查找左半部分;如果j>k,递归查找右半部分。

特别地,快速排序的分区法实际上得到了k个最小元素和n-k个最大元素,而不仅仅是第k个元素。

int random_partition(int a[], int l, int u)
{
    swap(a, l, randint(l, u));
    int i = l;
    int j = u+1;
    int t = a[l];
    while (1) {
        do {
            i++;
        } while (a[i] < t && i <= u);
        do {
            j--;
        } while (a[j] > t);
        if (i > j) break;
        swap(a, i, j);
    }
    swap(a, l, j);
    return j;
}

int random_select(int a[], int l, int u, int k) 
{
    assert(l <= u && k>=1 && k <= u-l+1); //确保选择的第k小的数范围不超过数组大小
    if (l == u) return a[l];   //如果只有1个元素,可以直接返回
    int p = random_partition(a, l, u); //划分
    int i = p - l + 1;
    if (i == k)  //左边数目等于k,返回a[p]
        return a[p];
    else if (i < k) //左边数目小于k,从右边选择k-i小的元素
        return random_select(a, p+1, u, k-i);
    else  //左边数目大于k,从左边选择第k小的元素
        return random_select(a, l, p-1, k);
}

扩展问题1:如果是海量数据,如10G数据,1G内存,求数组中的第k大的数。

复杂:O(nlogk)

解法:哈希Hash(将大文件切割为小文件)+ K容量最小堆 + 多结果合并

其中,维持一个大小为k的小顶堆,遍历一次数组,如果数组中的元素比堆顶的元素大,那么就更新堆。最后堆中存放的是数组中的前k大元素。堆顶元素即为要求的第k大个数。大多数情况下,堆可以全部载入内存。如果K仍然很大,我们可以尝试先找最大的K'个元素,然后找第K'+1个到第2 * K'个元素,如此类推(其中容量K'的堆可以完全载入内存)。不过这样,我们需要扫描所有数据ceil (K/K')次。


扩展问题2:如果是找第k到第m(0<k<=m<=n)大的数呢?
解法:可以用小根堆来先求出m个最大的,然后从中输出k到m个。


3.5 用异或找缺失的数

问题:【连续的数】1到n中缺失了一个数,顺序被打乱,找出缺失的数

复杂:O(n)

解法:

对于丢失一个数的情况: 
1)用1+2+...+n减去当前输入数据的总和。时间复杂度:O(n) 空间复杂度:O(1) 【容易溢出】
2)用12...*n除以当前输入数据的总积。时间复杂度:O(n) 空间复杂度:O(1) 【容易溢出】
3)用1^2^...^n的结果再逐个异或当前输入数据。时间复杂度:O(n) 空间复杂度:O(1)
4)对输入数据排序,然后从头到尾遍历一次。时间复杂度O(nlogn) 空间复杂度O(1)
5) 对输入数据进行Hash,然后从头到尾遍历一次。时间复杂度O(n) 空间复杂度O(n)

第三种方法,因为相同的数取异或为0,0^0为0,所以最终按照该方法得到的数就是缺失的那个数。其算法复杂度准确的说为:Theta(2*n-1)。

扩展问题1:【连续的数】1到n中多了一个数,顺序被打乱,找出多的数。

扩展问题2:【不连续的数】给你n个数,其中有且仅有一个数出现了奇数次,其余的数都出现了偶数次。用线性时间常数空间找出出现了奇数次的那一个数。 

解法1,2:仍可以用异或O(n)算法

扩展问题3:【连续的数】1到n中[缺失了/增加了]两个数,顺序被打乱,找出缺失的数。

扩展问题4:【不连续的数】给你n个数,其中有且仅有两个数出现了奇数次,其余的数都出现了偶数次。用线性时间常数空间找出出现了奇数次的那一个数。 

解法3,4:假定我们迷失的数字是S1,S2。那么我们全部异或之后得到的就是S1^S2的值。分析一下就可以知道,S1!=S2,也就是说S1^S2!=0; 这样也就是说S1^S2的这个值有二进制位有一位是1,那么我们就可以把这些所有的数字分成2组,一组这个二进制位是1,另一个这个二进制位是0的来重新做异或。这样就可以把其中一个S1求出来了,那再S1^(S1^S2)一下,S2也就得到了。

扩展问题5:【连续的数】1到n之间的n个数不重复乱序,有一个数x变成了y,求这俩数。

扩展问题6:【连续的数】给你n个数,其中有且仅有三个数出现了奇数次,其余的数都出现了偶数次。用线性时间常数空间找出出现了奇数次的那三个数。

3.6 查找数组中和为定值的两个数【2sum问题】

问题:能否快速找出一个数组中的两个数字,让这两个数字之和等于一个给定的数字,为了简化起见,我们假设这个数组中肯定存在至少一组符合要求的解。

出处:《编程之美》-2.12-快速寻找满足条件的两个数

复杂:O(nlogn)

解法1:【暴力穷举】O(n^2)

解法2:【预排序+二分查找】O(nlogn)
       对数组预排序O(NlogN),然后遍历该数组,对每一个数a,都进行查找Sum-a在不在数组中。这种查找在二分查找下需要O(logN),所以综上效率为O(NlogN)+O(N)*O(logN)=O(NlogN).

如下俩个数组:
1、 2、  4、7、11、15     //用15减一下为 
14、13、11、8、4、 0      //如果下面出现了和上面一样的数,稍加判断,就能找出这俩个数来了。
第一个数组向右扫描,第二个数组向左扫描。
解法3:【Hash】O(n)
       如果在一定的限制条件下,比如说1,数字均为int型,2,此中int型数的范围有一定限制。则可以考虑用hash来获得O(1)的查找效率。方法,首先遍历一遍数组,初始化hash表O(N),然后再次遍历数组,对每一个数字a进行查找SUm-a只需O(1),所以总的效率为:O(N)+O(N)*O(1)=O(N).

解法4:【预排序+对向遍历】O(nlogn)
      首先预排序O(NlogN).然后在一个循环体里使用两个循环变量进行反向遍历,并且这两个变量遍历的方向是不变的,从而保证遍历算法的时间复杂度为O(n)。我们先对数组排序,然后再数组的开始和结尾处维护两个游标i,j,然后我们比较arr[i]+arr[j]与sum的大小,如果arr[i]+arr[j]>sum,则i++,如果>sum,则j--,直到找到arr[i]+arr[j]=sum,这样,复杂度即为遍历O(N),排序O(NlogN)。

刚开始一直无法理解这样子一定可以找到这个和么?难道不会漏掉了解得位置。可以这么理解,假如排好序后的数组为1,3,6,a,9,12,17,28,b,35,46  ,那么i最初指向1的位置,j最初指向46的位置,比如所求的是Sum=a+b,a<b,ab在其中数组中某位置上。那么i和j在变化过程中,只考虑i遇到了a或者j遇到了b的时候,必定有一个先遇到,比如i遇到了a,那么这个时候j必定还没指到b,故这是j指到的比b大,从而j减小直到b位置。同理若j先指到了b位置,那么i必定还没指到a(这是我们的前提),然后i现在指到的比a小,故,i增加,直到a位置。
     这个是个很奇妙的过程。

//2sum问题,一个未排序数组中找和为sum的所有数对(a,b)
void twoSum(int arr[], int length, int sum){
	sort(arr,arr+length);
	int i=0;
	int j=length-1;
	while(i<j){
		if(arr[i]+arr[j]==sum){
			cout<<arr[i]<<":"<<arr[j]<<endl;
			i++;
			j--;
		}
		else if(arr[i]+arr[j]<sum)
			i++;
		else // arr[i]+arr[j]>sum
			j--;
	}
}

3.7 查找数组中和为定值的三个数【3sum问题】

扩展问题:如果把这个问题的“两个数字”改为“三个数字”或“任意多的数字”,怎么解?【3sum问题】
解法时间复杂度为O(n^2)。如果数组已排序,利用解法1的双指针遍历法,可以在O(n)的时间内找到两个数之和等于一个給定的数。我们假设找到的三个数ai,aj,ak有ai<=aj<=ak,要让ai+aj+ak=sum,也就是要ai+aj=sum-ak,设subsum=sum-ak,很容易发现subsum的值只有n个,而确定ai+aj=subsum中的ai,aj只需要O(n)的时间,所以总的时间复杂度为O(nlogn+n*n)=O(n^2)

即:三个数字:首先还是先对数组进行排序,然后从i=0到n-1进行遍历,遍历arr[i]时,在调用上面的函数getSumNum(arr , Sum-arr[i])即可。
另:任意m个数字的想法:
      首先还是先对数组进行排序,然后从i=0到n-1个元素遍历,遍历arr[i]时,在剩下的n-1个元素中调用getSumNum(arr,Sum-arr[i]),此时为求m-1个元素和为Sum-arr[i];接下来,同样的方法,从j=0到n-2个元素遍历,遍历arr[j]时在arr上递归调用getSumNum(arr,Sum-arr[i]-arr[j]),此时为求m-2个元素和为Sum-arr[i]-arr[j];依次递归,直到为求2个元素和为Sum-?-?-?...时为止。
另,不论是求3个数字还好是m个数字,总是能比较穷举法少一个数量级n,比先排序然后二分查找求Sum-arr[i]也要快。

扩展问题:如果输入的数组是没有排序的,但知道里面数字的范围,其他条件不变,如何在O(n)时间里找到这两个数字?

解法:扫一遍桶排序,然后前后再扫一遍。

http://zhedahht.blog.163.com/blog/static/25411174201131184017844/

//3sum问题,一个未排序数组中找和为sum的所有数对(a,b.c)
void threeSum(int arr[], int length, int sum){
	sort(arr,arr+length);
	for(int i=0;i<length;i++){
		int j=i+1;
		int k=length-1;
		while(j<k){
			if(arr[j]+arr[k]==sum-arr[i]){
				cout<<arr[i]<<":"<<arr[j]<<":"<<arr[k]<<endl;
				j++;
				k--;
			}
			else if(arr[j]+arr[k]<sum-arr[i])
				j++;
			else  //arr[j]+arr[k]>sum-arr[i]
				k--;
		}
	}
}

3.8 数组循环移位

问题:设计一个算法,把一个含有N个元素的数组循环右移K位,要求时间复杂度为O(N),且只允许使用两个附加变量,如abcd1234循环右移三位,则为 abcd11234 ->4abcd123->34abcd12->234abcd1

出处:《编程之美》-2.17-数组循环移位

复杂:O(n)

解法:

假设原数组序列为abcd1234,要求变换成的数组序列为1234abcd,即循环右移了4位。比较之后,不难看出,其中有两段的顺序是不变的:1234和abcd,可把这两段看成两个整体。右移K位的过程就是把数组的两部分交换一下。变换的过程通过以下步骤完成:即 【mn->m'n'->(m'n')'=nm】
1.   逆序排列abcd:abcd1234 → dcba1234;
2.   逆序排列1234:dcba1234 → dcba4321;
3.   全部逆序:dcba4321 → 1234abcd。

Reverse(int* arr, int b, int e)
{
    for(; b < e; b++, e--)
    {
        int temp = arr[e];
        arr[e] = arr[b];
        arr[b] = temp;
    }
}
RightShift(int* arr, int N, int k)
{
    K %= N;
    Reverse(arr, 0, N – K - 1);
    Reverse(arr, N - K, N - 1);
    Reverse(arr, 0, N - 1);
}
其中,由于很可能K>N不一定,所以使用语句;K=K%N;降低复杂度。


4,数学数字问题

4.1 求二进制数中1的个数

问题:对于一个字节的无符号整形变量,求其二进制表示中“1”的个数,要求算法的执行效率尽可能高。

出处:《编程之美》-2.1-求二进制数中1的个数

复杂:O(n)

解法1:利用整形数据除法的特点,通过相除和判断余数的值来分析

int Count(BYTE v)  
{  
     int num = 0;  
     while(v)  
     {  
          if(v % 2 == 1)  
          {  
               num++;  
          }  
          v = v/ 2;  
     }   
     return num;  
} 
解法2:利用向右移位操作来达到上面的相处的效果
int Count(BYTE v)  
{  
     int num = 0;  
     while(v)  
     {  
          num += v & 0x01;  
          v >>= 1;  
     }  
     return num;  
} 
解法3: 利用"v&(v-1)"来判断一个数是2^n【精妙,复杂度只与1的个数有关】

int Count(BYTE v)  
{  
     int num = 0;  
     while(v)  
     {  
          v &= (v-1);  
          num++;  
     }  
     return num;  
}  
解法5:打表:

因为只是一个BYTE的整数,范围为0~255,索性预存一个256个数的数组countTable[256],保存每个数对应的1的个数会是多少,例如countTable[8]=3。

扩展问题:给定两个正整数A,B。判断A和B中有多少位是不同的?
解答:对A和B做异或,之后再数其中1的数量,Count(A ^ B),复杂度O(1+A和B位数不同的数量)。

4.2 阶乘n!末尾0的个数

问题:给定一个整数n,阶乘n!末尾有多少个0

出处:《编程之美》-2.2-不要被阶乘吓倒

复杂:O()

解法:问题转化为求N!中因子5的个数。

解法1:暴力法:把N!从1到N的每个数遍历一遍,算出每个数有多少个5组成。

ret = 0;
for(i = 1; i <= N ; i++)
{
      j = i;
      while(j % 5 == 0)
      {
              ret++;
              j /= 5;  
       }
}
解法2:公式法:求N!中5的因子的个数Z

公式:Z = [N/5] +[N/5^2] +[N/5^3] + …(不用担心这会是一个无穷的运算,因为总存在一个K,使得5^K > N,[N/5^K]=0。)
[N/5] 表示不大于N的的数中5的倍数贡献一个5, [N/52] 表示不大于N的数中5^2的倍数再贡献一个5……

ret = 0;
while(N)
{
         ret += N / 5;
         N /= 5;
}

扩展问题:求N!的二进制表示中最低位1的位置

解法:问题转化为求N!中因子2的个数。

解法1:公式法:求N!中2的因子的个数Z

公式:Z = [N/2]+[N/4]+[N/8]+[N/16] +…

int lowestOne(int N)
{
    int ret;
    while(N)
    {
        N >>= 1;
        ret += N;
    }
    return ret;
}


4.3 求解最大公约数

问题:给定整数a,b,求最大公约数gcd(a,b)

出处:《编程之美》-2.7-最大公约数问题

复杂:O()

解法1:欧几里得辗转相除法

基于原理:gcd(x, y)= gcd(y, y % x)(y > 0)

int gcd(int x, int y)
{
    return (!y)?x:gcd(y, x%y);
}
解法2:辗转相减法
基于原理:模操作是瓶颈,设f(x,y)表示x,y的最大公约数,则有x-y也可以被k整除,即f(x,y)=f(y,x-y)   (x>=y>0) 。如果x<y那么就可以先交换(x,y)因为(f(x,y)=f(y,x)),从而避免求一个正数和一个负数的最大公约数情况。

BigInt  gcd(BigInt  x,BigInt  y)
{
 if(x<y)
  return gcd(y,x);
 return (!y)?x:gcd(x-y,y);
}
缺点:如果是f(100000000000,1)就郁闷了


解法3:结合解法1和解法2

解法一的问题在于计算负责的大整数的除法,解法二的问题在于迭代次数太多,对于x,y如果x=k*x,y=k*y,那么f(y,x)=k*f(y1,x1),(具体过程看编程之美P152页)。另外如果x=p*x1,假设p是素数,并且y%p!=0(即y不能被p整除),那么f(x,y)=f(p*x1,y)=f(x1,y),注意到以上两点后,我们就可以对这两点算法进行改进。

最简单的方法:我们知道2是一个素数
1.若x,y都是偶数,f(x,y)=2*f(x/2,y/2)=2(x>>1,y>>1)
2.若x为偶数,y为奇数,f(x,y)=f(x/2,y)=f(x>>1,y)
3.若x为奇数,y为偶数f(x,y)=f(x,y/2)=f(x,y>>1)
4.若x为奇数,y为奇数f(x,y)=f(x,x-y),(x-y)之后是一个偶数,下一步一定会有除以2的操作

上面算法的时间复杂度为0(log2(max(x,y))).

//BigInt为自己实现的一个大整数类,IsEven()判断是否为偶数 
BigInt gcd(BigInt x,BigInt y)
{
	if(x < y)
	  return gcd(y,x);
    if(y == 0)
       return x;
    else{
       if(IsEven(x))
	   {
	   	  if(IsEven(y)) //情形1,x,y均为偶数 
	   	    return 2 * gcd(x >> 1, y >> 1);
          else  //情形2,x为偶,y为奇 
            return gcd(x >> 1, y);
	   }
	   else
	   {
	   	  if(IsEven(y)) //情形3,x为奇,y为偶 
	   	    return gcd(x, y >> 1);
  	      else  //情形4,x,y均为奇 
  	        return gcd(y, x - y);
	   }
    }
}


4.4 逆转一个整数的二进制表示

问题:逆转一个整数的二进制表示,如把00001010变成01010000。

出处:《编程之美》-2.7-最大公约数问题


4.5 位运算实现两个整数的加减乘除运算

http://blog.csdn.net/luckyxiaoqiang/article/details/6886489


5,字符串算法

5.1 atoi算法

int atoi(char * s){
	int sig = 1;
	if(s[0]=='-'){
		sig=-1;
		s++;
	}
	int result = 0;
	while(*s){
		result=result*10+(*s-'0');
		s++;
	}
	result*=sig;
	return result;
}

5.2 itoa算法

char* itoa(int num, char *str, int radix){
	char * t = str;
	int sig = 1;
	if(num<0){
		sig = -1;
		num = -num;
		*t++='-';
	}
	char * p = t;
	while(num){
		*t++ = num%radix+'0';
		num/=radix;
	}
	*t = '\0';
	char * q = t-1;

	while(p<q){
		char temp = *p;
		*p = *q;
		*q = temp;
		p++;
		q--;
	}
	return str;
}

6,其他算法

6.1 主定理


6.2 全排列

void swap(int &a, int &b)
{
	int t = a;
	a = b;
	b = t;
}

//一个数组n项,从第i项开始排列a[i],a[i+1],...,a[n-1]
void perm(int a[], int i, int n)
{
	//打印
	if(i==n)
	{
		for(int i=0;i<n;i++)
			cout<<a[i]<<" ";
		cout<<endl;
	}

	for(int j=i;j<n;j++)
	{
		swap(a[i],a[j]);
		perm(a,i+1,n);
		swap(a[i],a[j]);
	}

}

6.3 判断质数

bool isPrime(int n){
	assert(n>=1);
	int mid = sqrt((double)n);
	for(int i=2;i<=mid;i++){
		if(n%i==0){
			return false;
		}
	}
	return true;
}



     

问题:

出处:

复杂:O(n)

解法:

代码:

输入两个串:
 string midOrder = "HDIBJEKALFMCNGO";
 string firstOrder = "ABDHIEJKCFLMGNO";
输出一棵二叉树。 
算法思想很简单,在先序中的第一个节点一定是根节点,此节点在中序中的位置可以将中序分为左右两棵子树。如:
根为A,中序分为:HDIBJEK     A   LFMCNGO,这两棵子树在使用同样的方法就生成一棵树。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值