一、写随笔的原因:排序比较常用,借此文介绍下排序常用的算法及实现,借此来MARK一下,方便以后的复习。(本人总是忘得比较快)
二、具体的内容:
1.插入排序
插入排序:在前面已经排好序的序列中找到合适的插入位置。
插入排序又可细分为:直接插入排序,二分法插入排序,希尔排序
(1)直接插入排序:简单来说,就是在每一步将一个待排序的数据,从后向前找到合适的位置插入,知道全部数据插入完,排序结束。(从第二个数开始,第一个数插入时没有其他数据,所以直接不用管)
Java代码实现如下:
/* 直接插入排序 */ public static int[] directInsertSort(int[] a) { // 实现从小到大排序 for (int i = 1; i < a.length; i++) { int temp = a[i]; // 存入当前待排序的数据 int j; for (j = i - 1; j >= 0; j--) { // 从后向前依次比较,插入到合适的位置 // (a[j]大于temp往后移动一位) if (temp < a[j]) { a[j + 1] = a[j]; } else { break; // a[j]不大于temp,则找到了合适的位置,直接退出循环 } } a[j + 1] = temp; // 把当前待排序的数据放入到找的位置(因为是从大到小所以放到j的后面) } return a; }
(2)二分法插入排序:思想都是插入排序的思想,只是在查找合适位置时是通过二分法来找位置的。
下面有个图可以很好的解释(盗图的):
Java代码实现如下:
/* 二分法插入排序 */ public static void binaryInsertSort(int[] a) { for (int i = 0; i < a.length; i++) { int temp = a[i]; // 存入当前待排序的数据 int low = 0; //low的下标 int high = i - 1; //high的下标 int mid = 0; //mid下标 while (low <= high) { mid = (low + high) / 2; //算出mid位置 if (temp < a[mid]) { // temp小于mid位置的值,则继续与左边部分的位置使用二分 high = mid - 1; // 这时候的右下标需要修改为mid前一位 } else { // temp不小于mid位置的值,则继续与右边边部分的位置使用二分 low = mid + 1; // 这时候的右下标需要修改为mid后一位 } } // 找到位置后需要将原先位置上及后面的的数据向后平移一位,腾出位置插入 for (int j = i - 1; j >= low; j--) { a[j + 1] = a[j]; } if (low != i) { //low == i,则不需要变化,本来的位置就是正确的 a[low] = temp; } } }
(3)希尔排序:先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2,直重复上述分组和排序操作;至di=1,即所有记录放进一个组中排序为止。
Java代码实现如下:
public static void shellSort(int[] a) { int d = a.length; // 不断缩小d,直至d=1 while (true) { d = d -1; for (int x = 0; x < d; x++) { for (int i = x + d; i < a.length; i = i + d) { int temp = a[i]; int j; // 相当于把直接插入排序的1变成了d for (j = i - d; j >= 0 && a[j] > temp; j = j - d) { a[j + d] = a[j]; } a[j + d] = temp; } } if (d == 1) { break; } } }
2.选择排序
选择排序:在剩余的待排序记录序列中找到最小数据。
选择排序又可细分为:直接选择排序,堆排序。
(1)直接选择排序:在要排序的一组数中,选出最小的一个数与第一个位置的数交换;然后在剩下的数当中再找最小的与第二个位置的数交换,如此循环到倒数第二个数和最后一个数比较为止。
如下图:
Java代码实现如下:
/* 直接选择排序 */ public static void directSelectSort(int[] a) { for (int i = 0; i < a.length; i++) { int min = a[i]; int n = i; // 最小数的索引 for (int j = i + 1; j < a.length; j++) { if (a[j] < min) { // 找出最小的 min = a[j]; n = j; } } // 最小的一个数与i位置的数交换 a[n] = a[i]; // n为最小的一个数的索引 a[i] = min; } }
(2)堆排序:堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。
首先简单了解下堆结构:堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:
同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子:
我们用简单的公式来描述一下堆的定义就是:
大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
堆排序的基本思想是:
1.将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。
2.将其与末尾元素进行交换,此时末尾就为最大值。
3.然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。
代码实现如下:
/* 堆排序 */ public static void heapSort (int[] arr) { //1.构建大顶堆 for(int i=arr.length/2-1;i>=0;i--){ //从第一个非叶子结点从下至上,从右至左调整结构 adjustHeap(arr,i,arr.length); } //2.调整堆结构+交换堆顶元素与末尾元素 for(int j=arr.length-1;j>0;j--){ swap(arr,0,j);//将堆顶元素与末尾元素进行交换 adjustHeap(arr,0,j);//重新对堆进行调整 } } /** * 调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上) * @param arr * @param i * @param length */ public static void adjustHeap(int []arr,int i,int length){ int temp = arr[i];//先取出当前元素i for(int k=i*2+1;k<length;k=k*2+1){//从i结点的左子结点开始,也就是2i+1处开始 if(k+1<length && arr[k]<arr[k+1]){//如果左子结点小于右子结点,k指向右子结点 k++; } if(arr[k] >temp){//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换) arr[i] = arr[k]; i = k; }else{ break; } } arr[i] = temp;//将temp值放到最终的位置 } /** * 交换元素 * @param arr * @param a * @param b */ public static void swap(int []arr,int a ,int b){ int temp=arr[a]; arr[a] = arr[b]; arr[b] = temp; }
三、总结:
本文主要是对插入排序和选择排序进行了讲解,并通过代码来实现。还有一些像交换排序,归并排序,基数排序等就作为第二部分内容在下次在写吧。