--- layout: post title: 八种排序 tags: 算法 algorithm 排序 categories: algorithm --- * TOC {:toc} ![八大排序][八大排序]
## 1.直接插入排序 基本思想:在要排序的一组数中,假设前面(n-1) [n>=2] 个数已经是排好顺序的,现在要把第n个数插到前面的有序数中,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。 ![插入排序][插入排序] ~~~java /** * 形象描述:踢馆 * 直接插入排序(从小到大) * @param src 待排序的数组 */ public void straight_insertion_sort(int[] src){ int tmp; for (int i = 0; i < src.length; i++) { tmp = src[i]; int j = i - 1; //向前匹配,凡是比自己大的都往后移一位,碰到不比自己大的直接跳出 for (; j >= 0 && tmp ## 2.希尔排序(缩小增量排序) 基本思想:算法先将要排序的一组数按某个增量d(n/2,n为要排序数的个数)分成若干组,每组中记录的下标相差d.对每组中相同位置的元素进行直接插入排序,然后再用一个较小的增量(d/2)对它进行分组,再对每组中相同位置的元素进行直接插入排序。当增量减到1时,进行直接插入排序后,排序完成。 ![希尔排序][希尔排序] ~~~java /** * 希尔排序(缩小增量排序)(从小到大) * @param src */ public void shell_sort(int[] src){ int inc = src.length; int tmp; while(true){ inc = (int)Math.ceil(inc/2);//增量,亦即每组长度 //遍历每组进行排序的元素索引,具体看内部注释 for (int x = 0; x < inc; x++) { //以下内容即插入排序,但是搜索头步进inc,回顾时步退inc //这是一个模拟,可以想象为:将src分组,每组长度为inc,然后对每组的第0,1,2,3...个元素分别进行插入排序 //即:第二组的第一个元素与第一组的第一个元素进行插入排序,第三组的第一个元素与第一二组的第一个元素进行插入排序... // 第二组的第二个元素与第一组的第二个元素进行插入排序,第三组的第二个元素与第一二组的第二个元素进行插入排序... // ... for (int i = x + inc; i < src.length; i += inc) { tmp = src[i]; int j = i - inc; for (; j >= 0 && tmp < src[j]; j-=inc) { src[j + inc] = src[j]; } src[j + inc] = tmp; } } //不必担心增量为1时会完全等同于普通的直接插入排序,进而造成性能问题 //原因:在排序过程中匹配时遵循"小者胜,大者让"的原则,因此在搜索头进行回顾匹配时,会很快(1~2个元素左右)碰上不久前刚刚打败过自己的对手. if(inc == 1){ break; } } } ~~~
## 3.简单选择排序 基本思想:在要排序的一组数中,选出最小的一个数与第一个位置的数交换;然后在剩下的数当中再找最小的与第二个位置的数交换,如此循环到倒数第二个数和最后一个数比较为止。 ![简单选择排序][简单选择排序] ~~~java /** * 简单选择排序(从小到大) * @param src */ public void select_sort(int[] src){ int pos,tmp,j; //定义最小值索引,最小值缓存,最小值检索起点索引 //在每个i位置放入合适的值:i==0时放最小值;i==1时放第二小的值... for (int i = 0; i < src.length; i++) { //假定i位置的值就是合适的值 tmp = src[i]; pos = i; //检索i之后的所有元素(前面的元素都比i位置的小),找到一个最小的,与i位置的元素换位 for (j = i+1; j < src.length; j++) { if(src[j] < tmp){ tmp = src[j]; pos = j; } } src[pos] = src[i]; src[i] = tmp; } } ~~~
## 4.堆排序 基本思想:构建一个大顶堆完全二叉树,此时整个二叉树是无序的,二叉树中的无序部分称为堆,堆顶为最大值。将堆中的末尾节点与堆顶节点互换,将刚换到末尾的节点踢出堆(刚被换到末尾的节点与之前被换到末尾的节点之间将是有序的),调整剩余节点,使其满足大顶堆的要求。以此类堆,最后堆将消失,得到一组正序排列的节点。 建堆: ![堆排序1][堆排序1] 交换,从堆中踢出最大数 ![堆排序2][堆排序2] 剩余结点再建堆,再交换踢出最大数 ![堆排序3][堆排序3] ~~~java /** * 调整堆 * @param src 存放节点的数组,节点号从1开始 * @param i 调整起点节点号 * @param size 有效节点长度 */ private void adjHeap(int[] src,int i,int size){ int lc = 2*i;//左子节点号 int rc = 2*i + 1;//右子节点号 int mx = i;//最大值节点号 //非叶节点最大序号为size/2 if(i<=size/2){ //如果左子节点存在,且大于最大节点 if(lc<=size && src[lc-1]>src[mx-1]){ mx = lc; } //如果右子节点存在,且大于最大节点 if(rc<=size && src[rc-1]>src[mx-1]){ mx = rc; } //表示节点关系需要变化 if(mx!=i){ swap(src,mx,i); adjHeap(src,mx,size); } } } /** * 交换节点 * @param src 存放节点的数组,节点号从1开始 * @param p 前一个节点号 * @param n 后一个节点号 */ private void swap(int[] src,int p, int n){ int tmp = src[p-1]; src[p-1] = src[n-1]; src[n-1] = tmp; } /** * 创建初始堆 * @param src 存放节点的数组,节点号从1开始 * @param size 有效节点长度 */ private void buildHeap(int[] src, int size){ for(int i=size/2; i>=1; i--){ adjHeap(src,i,size); } } /** * 最大堆排序(从小到大) * @param src */ public void heap_sort(int[] src){ int size = src.length; buildHeap(src,size); for(int i=size; i>=1; i--){ swap(src,1,i); adjHeap(src,1,i-1); } } ~~~
## 5.冒泡排序 基本思想:在要排序的一组数中,对当前还未排好序的范围内的全部数,由始至终对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒。 ![冒泡排序][冒泡排序] ~~~java /** * 冒泡排序(从小到大) * @param src */ public void bubble_sort(int[] src){ int tmp; //移动测头 for (int i = 0; i < src.length; i++) { //从测头向前推进,若相邻元素前者大于后者,则两者换位 for (int j = i; j > 0; j--) { if(src[j] < src[j-1]){ tmp = src[j]; src[j] = src[j-1]; src[j-1] = tmp; } } } } ~~~
## 6.快速排序 基本思想:选择一个基准元素,通常选择第一个元素或者最后一个元素,通过一趟扫描,将待排序列分成两部分,一部分比基准元素小,一部分大于等于基准元素,此时基准元素在其排好序后的正确位置,然后再用同样的方法递归地排序划分的两部分。 ![快速排序][快速排序] ~~~java /** * 快速排序(从小到大) * @param src */ public void quick_sort(int[] src){ doSort(src,0,src.length-1); } /** * @param src 数组对象 * @param bi 有效位开头 * @param ei 有效位结尾 * @return */ private int getMiddle(int[] src, int bi, int ei){ //缓存开头的元素作为中轴 int tmp = src[bi]; while(bi < ei){ //从右向左开始对比,不比中轴小的元素跳过,并继续向左推进 while(bi < ei && src[ei] >= tmp){ ei--; } //能够执行到这里,表示src[ei]= ei) return; int mid = getMiddle(src,bi,ei); doSort(src,bi,mid-1); doSort(src,mid+1,ei); } ~~~
## 7.归并排序 基本思想:归并排序法是将两个以上有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列 ![归并排序][归并排序] ~~~java /** * @param src 数组对象 * @param bi 有效位开头 * @param ei 有效位结尾 */ private void doSort(int[] src, int bi, int ei){ if(bi >= ei) return; int mid = (bi + ei)/2; doSort(src, bi, mid); doSort(src, mid + 1, ei); merge(src, bi, mid, ei); } /** * @param src 数组对象 * @param bi 有效位开头 * @param mid 有效位中点 * @param ei 有效位结尾 */ private void merge(int[] src, int bi, int mid, int ei){ int[] tmp = new int[ei-bi+1]; int ti = 0; //tmp数组的索引 int fbi = bi; //前半段的起点 int bbi = mid + 1;//后半段的起点 while (fbi<=mid&&bbi<=ei) { if(src[fbi] <= src[bbi]){ //更小的元素将先被放入tmp,然后再对比下一个 //因为是递归方法,所以前半段是有序的,后半段也是有序的, //所以前半段的第一个元素比后半段的第一个元素小,那么前半段的第一个元素一定是最小的 //比较大小往tmp中插入的过程其实类似选择排序+希尔排序 tmp[ti++] = src[fbi++]; }else{ tmp[ti++] = src[bbi++]; } } //有序元素放入tmp后,前半段的游标可能没有到头(因为后半段的可能先到头了) while (fbi<=mid){ tmp[ti++] = src[fbi++]; } //有序元素放入tmp后,后半段的游标可能没有到头(因为前半段的可能先到头了) while (bbi<=ei){ tmp[ti++] = src[bbi++]; } //用tmp中排好序的元素替换掉src中指定范围的元素 for (ti = 0; ti < tmp.length; ti++) { src[bi+ti] = tmp[ti]; } } public void merge_sort(int[] src){ doSort(src,0,src.length-1); } ~~~
## 8.基数排序 基本思想:创建0-9十个容器,对数组中每个数取其相同位的数字,将数组元素放入到对应的容器中,然后按容器顺序从容器中取出放回数组,以此类推当位数递增到最大数的位宽时,排序完成 ![基数排序][基数排序] ~~~java public void radix_sort(int[] src){ //取最大值 int max = src[0]; for (int i = 0; i < src.length; i++) { max = src[i] > max ? src[i] : max; } //取最大值的位数 int w = 0; while (max>0){ max/=10; w++; } //创建十个队列,用二维数组模拟 int[][] bkt = new int[10][src.length]; //记录每个bkt队列中有几个src元素 int[] cm = new int[10]; for (int i = 0; i < w; i++) { //遍历元素,按元素的第i位数值分类排放 for (int j = 0; j < src.length; j++) { int item = src[j]; //求src中第j(从0开始)个元素的第i(从0开始)位数字 //例如987的第1位数字是(987%100)/10 int l = (item % (int)Math.pow(10,i+1))/(int)Math.pow(10,i); //bkt第l个队列可能有多个数据,用现有的数量表示位置 bkt[l][cm[l]] = item; //位置后移1位 cm[l] += 1; } //重新取出放回src //记录已经取出的个数 int count = 0; for (int j = 0; j < 10; j++) { //第i位为j的元素的个数 int cml = cm[j]; while (cm[j] > 0){ //j从小到大递增,这样取数据将使src具有递增趋势 //将这样的元素存入bkt,将使bkt中每个队列都具有递增趋势 //为了不破换这样的趋势,将需要从每个bkt队列的开头开始取数据 src[count] = bkt[j][cml-cm[j]]; //减去一个元素 cm[j] -= 1; count++; } } } } ~~~ [八大排序]:http://my.csdn.net/uploads/201205/12/1336792573_1570.png [插入排序]:http://my.csdn.net/uploads/201205/12/1336792682_4218.png [希尔排序]:http://my.csdn.net/uploads/201205/12/1336792712_3691.png [简单选择排序]:http://my.csdn.net/uploads/201205/12/1336793705_2932.png [堆排序1]:http://my.csdn.net/uploads/201205/12/1336793950_6973.png [堆排序2]:http://my.csdn.net/uploads/201205/12/1336793977_2095.png [堆排序3]:http://my.csdn.net/uploads/201205/12/1336793996_7183.png [冒泡排序]:http://my.csdn.net/uploads/201205/12/1336807392_9769.png [快速排序]:http://my.csdn.net/uploads/201205/12/1336807447_1275.png [归并排序]:http://my.csdn.net/uploads/201205/12/1336807959_9188.png [基数排序]:http://my.csdn.net/uploads/201205/12/1336807988_2428.png