排序算法总结
2012-10-07 18:50
218 查看
一、拓扑排序
拓扑排序的步骤:(1)从图中选择一个入度为0的顶点且输出之;
(2)从图中删掉该顶点及其所有以该顶点为弧尾的弧;
反复执行这两个步骤,直到所有的顶点都被输出,输出的序列就是这个无环有向图的拓扑序列。
二、递归归并排序、二路归并排序
归并算法的两种方法:1、使用分治法的递归归并算法:
/*递归归并排序 *将有二个有序数列list[first...mid]和list[mid+1,...last]合并 *list:待排序数组 *first:子序列1的下界 *mid:子序列1的上界 *last:子序列2的上界 *temp:临时保存数组 */ void Merge(element list[], int first, int mid, int last, element temp[]) { int i = first, j = mid + 1; int m = mid, n = last; int k = 0; while (i <= m && j <= n) { if (list[i] < list[j]) temp[k++] = list[i++]; else temp[k++] = list[j++]; } while (i <= m) temp[k++] = list[i++]; while (j <= n) temp[k++] = list[j++]; for (i = 0; i < k; i++) list[first + i] = temp[i]; return ; } /*递归归并排序 *分治,完成递归归并 *list:待排序数组 *first:list下界 *last:list上界 *temp:临时保存数组 */ void mergesort(element list[], int first, int last, element temp[]) { if (first < last) { int mid = (first + last) / 2; mergesort(list, first, mid, temp); //左边有序 mergesort(list, mid + 1, last, temp); //右边有序 Merge(list, first, mid, last, temp); //再将二个有序数列合并 } return ; } /*递归归并排序 *list:待排序数组 *n:为数组长度 */ bool MergeSort(element list[], int n) { element *p = new element ;//空间复杂度为O(n) if (p == NULL) exit(1); mergesort(list, 0, n - 1, p); delete[] p; return true; }
2、二路归并算法:
/*归并算法二,二路归并 *list:待排序数组 *n:为数组长度 *temp:临时保存数组 *k:为有序子数组的长度,一次二路归并排序后有序子序列存于temp数组中 */ void Merge2(element list[], int n, element temp[], int k) { int i, j; int m=0; int low1=0;//第一有序的下界 int up1, low2, up2;//第一有序的上界,第二有序的下界,第二有序的上界 /*将原始数组中足够分为两组的数据元素二路归并存放到数组temp中*/ while(low1+k < n-1) { low2 = low1+k; up1 = low2-1; up2 =(low2+k-1 < n-1) ? low2+k-1:n-1; for(i=low1, j=low2; i<=up1 && j <=up2; ) { if (list[i] <= list[j]) temp[m++] = list[i++]; else temp[m++] = list[j++]; } while (i <= up1) temp[m++] = list[i++]; while (j <= up2) temp[m++] = list[j++]; low1 = up2+1; } /*将原始数组中只够一组的数据元素顺序存放到数组temp中*/ i=low1; while(i<n) { temp[m++] = list[i++]; } return; } /*二路归并排序 *list:待排序数组 *n:为数组长度 */ bool MergeSort2(element list[], int n) { element *p = new element ;//空间复杂度为O(n) if (p == NULL) exit(1); int k=1;//二路归并长度从1开始 int i=0; while(k<n) { Merge2(list, n, p, k);//每次归并后保存在p中 for(i=0; i<n; i++) list[i] = p[i];//从p中拷贝到list中 k *=2;//归并长度加倍 } delete[] p; return true; }
三、希尔排序
/*@希尔排序:分组插入排序 *@list:待排序数组 *@n:总排序元素个数 */ void shellSort(element list[], int n)//希尔排序 { int span=n; element temp; while(span > 1)//跨度为1时排序结束 { span=(span+1)/2; for(int i=0; i<n-span; i++) { if(list[i+span]<list[i]) { temp=list[i+span]; list[i+span]=list[i]; list[i]=temp; } } } }
四、插入排序、选择排序 、冒泡排序
/* *@list:待排序数组 *@n:总排序元素个数 */ void insertSort(element list[], int n)//插入排序 { int i=0; int j=0; element next; for(i=0; i<n; i++) { next=list[i]; for(j=i-1;j>=0&&list[j]>next;j--)//j>=0且list[j]是和next比较 { list[j+1]=list[j];//后一位等于前一位 } list[j+1]=next; } return; } /* *@list:待排序数组 *@n:总排序元素个数 */ void selectSort(element list[], int n)//选择排序 { int i=0; int j=0; int k=0; element temp; for(i=0; i<n; i++) { temp=list[i]; k=i;//不要忘记 for(j=i+1; j<n; j++) { if(list[j]<temp) { temp=list[j]; k=j; } } list[k]=list[i]; list[i]=temp; } return; } /* *@list:待排序数组 *@n:总排序元素个数 */ void bubbleSort(element list[], int n)//冒泡排序 { int i=0; int j=0; element temp; bool exchange;//标记本趟是否有交换 for(i=0; i<n; i++) { exchange=false;//本趟排序开始 for(j=0; j<n-i-1; j++)//注意j的范围 { if(list[j]>list[j+1])//j+1不能是后面排序好的元素,所以j<n-i-1 { temp=list[j]; list[j]=list[j+1]; list[j+1]=temp; exchange=true;//本趟排序发生交换 } if(!exchange)//本趟排序没有发生交换,说明前n-1个不用排序,提前终止算法 { return; } } } return; }
五、堆排序
/* *@list:待排序数组 *@root:根结点下标 *@n:总排序元素个数 */ void adjust(element list[], int root, int n)//调整最大堆方法一 { int child=2*root+1;//左孩子 element temp; while(child < n)//***while*** { if(child < n-1 && list[child]<list[child+1])//比较左右结点,得到较大的为child { //注意child < n-1 child++; } if(list[root] > list[child])//比较父节点与较大子节点child { break; } else{ /*交换父子节点*/ temp = list[root]; list[root]=list[child]; list[child]=temp; /*继续向下,特别要注意这里的*/ root=child; child = 2*child +1; } } } void adjust2(element list[], int root, int n)//调整最大堆方法二 { int child=2*root+1;//左孩子 element rootkey=list[root];//标记最初根节点结点 while(child < n)//***while*** { if(child < n-1 && list[child]<list[child+1]) { child++;//比较左右结点,得到较大的为child } if(rootkey > list[child])//比较最初根节点结点 与 此时的最大子结点 { break; } else{ //这里不是交换父子结点,和insertSort思想类似 list[root]=list[child];//父节点等于较大孩子结点 //分别向下移动 root=child; child = 2*child +1; } } list[root]=rootkey;//找到最初根结点的合适位置 } /* *@list:待排序数组 *@n:总排序元素个数 */ void heapsort(element list[],int n)//堆排序 { int i; element temp; /*初始化得到最大堆*/ for(i=n/2;i>=0;i--) //从n/2处依次向前调整 adjust(list,i,n); //或者采用 adjust2 for(i=n-1;i>0;i--)//i为剩余规模 { temp=list[i]; //交换根结点和i结点 list[i]=list[0]; list[0]=temp; //或者采用 adjust2 adjust(list,0,i);//重新调整,此时参数root为0,规模n为i } }
六、快速排序(最后修改)
//2012-07-16 void quickSort(element list[], int left, int right)//快速排序 { int i=left; int j=right; if(i >= j) //判断需要i<j return; element temp=list[i]; while(i<j) { while(i<j && list[j]>temp)//需要i<j j--; list[i]=list[j]; i++; while(i<j && list[i]<temp)//需要i<j i++; list[j]=list[i]; j--; } list[j+1]=temp;// 运行到此处j j+1 i quickSort(list,left,j); quickSort(list,i,right); } void quickSort2(element list[], int left, int right) { int i=left; int j=right; if(i>=j) return; element pivot=list[i]; element temp; j++;//以下i++,j--了一次,i是需要首先+1,但j不需要,所以此处需要提前+1,抵消 do{ do{ i++; }while( list[i]<pivot);//不需要i<j,便于之后的交换操作和子快速排序 do{ j--; }while(list[j]>pivot);//不需要i<j if(i<j) { temp=list[i]; list[i]=list[j]; list[j]=temp; } }while(i<j);//运行到此处,a[i]>pivot,a[j]<pivot,j=i-1; //一般情况结束时left...j,i...right [j]<pivot,[i]>pivot所以交换left和j, //特殊情况结束时left......right,i=j=right+1//1,2,3,4,5,6,7 //即结束时,i后的都大于pivot,j前的都小于pivot,其中[j]<pivot,[i]>pivot list[left]=list[j]; list[j]=pivot; quickSort(list,left,j-1); quickSort(list,j+1,right); }
下面的是对quickSort方法的修改:
删除了quickSort的18、24行,以及27、29、30进行对应修改。修改之后使得如下代码运行到23行时,i=j,这样思维更清晰。
★★★★★
void quickSort3(element list[], int left, int right)//快速排序,修改时间2012-09-18 16:40:40 { int i=left; int j=right; if(i >= j) //判断需要i<j return; element temp=list[i]; while(i<j) { while(i<j && list[j]>temp)//需要i<j j--; list[i]=list[j]; while(i<j && list[i]<temp)//需要i<j i++; list[j]=list[i]; } list[i]=temp;// 运行到此处i=j quickSort3(list,left,i-1); quickSort3(list,j+1,right); }
七、计数排序
计数排序是一种运行时间在输入的某种假设情况下可以为Θ(n)的算法,它的过程中没有比较环节。基本的思路就是假设输入序列中任意的元素x都满足x∈[0, k],且x和k都为整数。然后对每一元素x,都确定出序列中比它小的元素的个数,比如为n,则x排序后的位置就应当从n + 1处开始。实现的时候还需要考虑一些细节,比如序列中有几个元素大小相等,因此还需要对大小相等的元素个数进行计数,这样才能正确分配排序后各个元素的位置。
过程中用到了一个辅助序列C,C的大小为k + 1,从C[0]到C[k],它的索引i代表序列中可能出现的大小为i的数,C[i]表示这个数有多少个。下面是算法导论上的例子:
待排序的序列:A = [2, 5, 3, 0, 2, 3, 0, 3] 计数序列:C = [2, 0, 2, 3, 0, 1]
索引: 0 1 2 3 4 5
表示序列中0, 1, 2, 3, 4, 5的个数分别为2, 0, 2, 3, 0, 1。
C序列中此时已经隐含了原序列中各个元素应该存放的位置,比如C[0] = 2,意味着大小为0的元素应当占据位置1, 2,而大小为1的元素应当从3开始,但原序列中没有1,因此向后遍历,大小为2的元素从3开始,个数为2,因此占据位置3, 4,以此类推。C[i]从0到n - 1求和的结果就是原序列中比n小的元素个数,假设为m,因此n应当从m开始,一直到m
+ c
- 1。
代码:(2012年7月16日 10:22:13)
/* *@list:待排序数组,要求list[i]是大于等于0的整数; *@n:总排序元素个数 */ void countSort(element list[],int n)//计数排序 { int max=list[0]; for(int i=1; i<n; i++)//计算数组中最大元素 { if(list[i]>max) max=list[i]; } element* count; count =(element*)malloc(sizeof(element)*(max+1));//数组下标为[0,...max] if(count==NULL) exit(1);//申请空间失败 for(int i=0; i<=max; i++)//初试化计数数组 { count[i]=0; } for(int i=0; i<n; i++)//计数 { int index=list[i]; count[index]++; } int j=0; for(int i=0; i<=max; i++)//根据计算数组排序 { while(count[i]&&j<n) { list[j]=i; count[i]--; j++; } } free(count); count=NULL; }
八、二分查找
递归和非递归二分查找//非递归二分查找算法 /*list:待查找的有序数组 *key:需要查找的元素 *n:数组长度 *return:返回查找的位置下标,返回-1表示未找到 */ int binarySearch(element list[], element key, int n) { int left =0; int right = n-1; int mid=0; while(left <= right)// <=,不能遗漏等于的情况 { mid=left+(right-left)/2; if(list[mid] == key) return mid; else if(list[mid] < key) left=mid+1; else right=mid-1; } return -1; } //递归二分查找算法 /*list:待查找的有序数组 *key:需要查找的元素 *left:数组下界 *right:数组上界 *return:返回查找的位置下标,返回-1表示未找到 */ int binarySearch2(element list[], element key, int left, int right) { int mid=0; if(left<=right)// <=,不能遗漏等于的情况 { mid=left+(right-left)/2; if(list[mid] == key) return mid; else if(list[mid] < key) return binarySearch2(list, key, mid+1, right); else return binarySearch2(list, key, left, mid-1); }else{ return -1;/*注意如果没有这种情况,返回值是永远是 mid,*/ } }