|
| 1 | +# <a id="top"></a>排序算法 ✈ |
| 2 | +排序算法的分类 |
| 3 | + |
| 4 | + |
| 5 | + |
| 6 | +## 冒泡排序 |
| 7 | +冒泡排序算法通过多次比较和交换来实现排序,其排序流程如下: |
| 8 | +1. 对数组中的各数据,依次比较相邻的两个元素的大小。 |
| 9 | +2. 如果前面的数据大于后面的数据,就交换这两个数据。经过第一轮的多次比较排序后,便可以将最小的数据排好。 |
| 10 | +3. 再用同样的方法把剩余的数据逐个进行比较,最后便可以按照从小到大的顺序排好数组各数据。 |
| 11 | + |
| 12 | +核心代码: |
| 13 | +```java |
| 14 | +public void bubbleSort(int[] a,int n){ |
| 15 | +int i,j; |
| 16 | +for(i=0; i<n-1; i++){ |
| 17 | +boolean exchange = false; |
| 18 | +for(j=n-1; j>i; j--){ |
| 19 | +if(a[j]<a[j-1]){ |
| 20 | +int temp = a[j]; |
| 21 | +a[j]=a[j-1]; |
| 22 | +a[j-1]=temp; |
| 23 | +exchange = true; |
| 24 | +} |
| 25 | +} |
| 26 | +if(!exchange) |
| 27 | +return; |
| 28 | +} |
| 29 | +} |
| 30 | +``` |
| 31 | +完整示例代码:[bubble.java](./bubble.java) |
| 32 | +## 快速排序 |
| 33 | +快速排序算法通过多次比较和交换来实现排序,其排序流程如下: |
| 34 | +1. 首先设定一个分界值,通过该分界值来将数组分成两部分。 |
| 35 | +2. 将大于等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于等于分界值,而右边部分中各元素都大于等于分界值。 |
| 36 | +3. 然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分为左右两部分,同样将左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。 |
| 37 | +4. 重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序,再递归排好右侧部分的顺序。当左、右两部分各数据排序完成后,整个数组的排序也就完成了。 |
| 38 | + |
| 39 | +核心代码: |
| 40 | +```java |
| 41 | +public void quickSort(int[] a, int left, int right){// 快速排序算法 |
| 42 | +int mid,t; |
| 43 | +int rtemp,ltemp; |
| 44 | + |
| 45 | +ltemp = left; |
| 46 | +rtemp = right; |
| 47 | +mid = a[(left+right)/2];// 分界值 |
| 48 | +while(ltemp<rtemp){ |
| 49 | +while(a[ltemp]<mid){ |
| 50 | +ltemp++; |
| 51 | +} |
| 52 | +while(a[rtemp]>mid){ |
| 53 | +rtemp--; |
| 54 | +} |
| 55 | +if(ltemp<=rtemp){ |
| 56 | +t=a[ltemp]; |
| 57 | +a[ltemp]=a[rtemp]; |
| 58 | +a[rtemp]=t; |
| 59 | +ltemp++; |
| 60 | +rtemp--; |
| 61 | +} |
| 62 | +} |
| 63 | +if(ltemp==rtemp){ |
| 64 | +ltemp++; |
| 65 | +} |
| 66 | +if(left<rtemp){ |
| 67 | +quickSort(a, left, ltemp-1);// 递归调用 |
| 68 | +} |
| 69 | +if(ltemp<right){ |
| 70 | +quickSort(a, rtemp+1, right);// 递归调用 |
| 71 | +} |
| 72 | +} |
| 73 | +``` |
| 74 | +程序中首先确定分解值为数组中间位置的值,当然也可以选在其他位置,比如数组的第一个数据。然后按照快速排序法的思路进行处理。接着,通过递归调用,处理分界值左侧的元素和右侧的元素。 |
| 75 | +完整示例代码:[quick.java](./quick.java) |
| 76 | +## 选择排序 |
| 77 | +选择排序算法通过选择和交换来实现排序,其排序流程如下: |
| 78 | +1. 首先从原始数组中选择最小的1个数据,将其和位于第1个位置的数据交换。 |
| 79 | +2. 接着从剩下的n-1个数据中选择次小的1个数据,将其和第2个位置的数据交换。 |
| 80 | +3. 然后不断重复上述过程,直到最后两个数据完成交换。至此,便完成了对原始数组的从小到大的排序。 |
| 81 | + |
| 82 | +核心代码: |
| 83 | +```java |
| 84 | +public void bubbleSort(int[] a,int n){ |
| 85 | +int i,j; |
| 86 | +int index; |
| 87 | +for(i=0; i<n-1; i++){ |
| 88 | +index = i; |
| 89 | +for(j=i+1; j<n; j++){ |
| 90 | +if(a[index]>a[j]){ |
| 91 | +index = j; |
| 92 | +} |
| 93 | +} |
| 94 | +if(index != i){ |
| 95 | +int temp = a[index]; |
| 96 | +a[index]=a[i]; |
| 97 | +a[i]=temp; |
| 98 | +} |
| 99 | +} |
| 100 | +} |
| 101 | +``` |
| 102 | +完整示例代码:[select.java](./select.java) |
| 103 | +## 堆排序 |
| 104 | +堆排序(Heap Sort)算法是基于选择排序思想的算法,其利用堆结构和二叉树的一些性质来完成数据的排序。堆结构是一种树结构,准确的说是一个完全二叉树。在这个树中每个结点对应于原始数据的一个记录,并且每个结点应满足以下条件: |
| 105 | +- 如果按照从小到打的顺序排序,要求非叶结点的数据要大于或等于其左、右子结点的数据。 |
| 106 | +- 如果按照从大到小的顺序排列,要求非叶结点的数据要小于或等于其左、右子结点的数据。 |
| 107 | + |
| 108 | +#### 堆排序过程 |
| 109 | +一个完整的堆排序需要经过反复的两个步骤:构造堆结构和堆排序输出。下面首先分析如何构造堆结构。 |
| 110 | + |
| 111 | +构造堆结构就是把原始的无序数据按前面堆结构的定义进行调整。首先,需要将原始的无序数据放置到一个完全二叉树的各个结点中。 |
| 112 | +然后由完全二叉树的下层向上层逐层对父结点的数据进行比较,使父结点的数据大于子结点的数据,这里需要使用“筛”运算进行节点数据的调整,直到所有结点最后满足堆结构的条件为止。在执行筛运算时,值较小的数据将逐层下移。 |
| 113 | +## 插入排序 |
| 114 | +插入排序算法通过比较和插入来实现排序,其排序流程如下: |
| 115 | +1. 首先对数组的前两个数据进行从小到大的排序。 |
| 116 | +2. 接着将第3个数据与排好序的两个数据比较,将第3个数据插入合适的位置。 |
| 117 | +3. 然后,将第4个数据插入已排好序的前3个数据中。 |
| 118 | +4. 不断重复上述过程,直到把最后一个数据插入合适的位置。最后,便完成了对原始数组从小到大的排序。 |
| 119 | + |
| 120 | +核心代码: |
| 121 | +```java |
| 122 | +public void bubbleSort(int[] a,int n){ |
| 123 | +int i,j; |
| 124 | +int t; |
| 125 | +for(i=1; i<n; i++){ |
| 126 | +t=a[i]; |
| 127 | +j=i-1; |
| 128 | +while(j>=0 && t<a[j]){// 后移每个比t大的元素 |
| 129 | +a[j+1]=a[j]; |
| 130 | +j--; |
| 131 | +} |
| 132 | +a[j+1]=t; |
| 133 | +} |
| 134 | +} |
| 135 | +``` |
| 136 | +插入排序时,如果元数据已经基本有序,则排序的效率就可大大提高。另外,对于数量较小的序列使用直接插入排序,因需要移动的数据量较小,其效率也较高。 |
| 137 | + |
| 138 | +完整示例代码:[insert.java](./insert.java) |
| 139 | +## 折半插入/二分排序 |
| 140 | +折半插入算法是在插入排序算法的基础上,在已排序序列中选择插入位置时,优化使用二分查找方法,使得排序效率提升。 |
| 141 | +核心代码: |
| 142 | +```java |
| 143 | +public static void BinaryInsertSort(int[] a) { |
| 144 | + int low, high; |
| 145 | + int mid, temp; |
| 146 | + for (int i = 0; i < a.length; i++) { |
| 147 | + temp = a[i]; |
| 148 | + low = 0; |
| 149 | + high = i - 1; |
| 150 | + while (low <= high) {// 折半查找的过程 |
| 151 | + mid = (low + high) / 2; |
| 152 | + if (temp < a[mid]) |
| 153 | + high = mid - 1; |
| 154 | + else |
| 155 | + low = mid + 1; |
| 156 | + } |
| 157 | + for (int j = i - 1; j > high; j--) |
| 158 | + a[j + 1] = a[j]; |
| 159 | + a[high + 1] = temp; |
| 160 | + } |
| 161 | +} |
| 162 | +``` |
| 163 | +完整示例代码:[binaryInsert.java](./binaryInsert.java) |
| 164 | +## Shell排序 |
| 165 | +Shell排序算法严格来说基于插入排序的思想,其又称为希尔排序或者缩小增量排序。Shell排序算法的排序流程如下: |
| 166 | +1. 将有n个元素的数组分为n/2个数字序列,第1个数据和第n/2+1个数据为一对,第2个数据和第n/2+2个数据为一对,...... |
| 167 | +2. 一次循环使每一个序列对排好序 |
| 168 | +3. 然后,再变为n/4个序列,再次排序。 |
| 169 | +4. 不断重复上述过程,随着序列减少最后变为一个,也就完成了整个序列。 |
| 170 | + |
| 171 | +核心代码: |
| 172 | +```java |
| 173 | +public void bubbleSort(int[] a,int n){ |
| 174 | +int i,j,r; |
| 175 | +int t; |
| 176 | +for(r=n/2;r>=1;r/=2){// 设置间距r 分组比较 |
| 177 | +for(i=r; i<n; i++){ |
| 178 | +t=a[i]; |
| 179 | +j=i-r; |
| 180 | +while(j>=0 && t<a[j]){// 后移每个比t大的元素 |
| 181 | +a[j+r]=a[j]; |
| 182 | +j-=r; |
| 183 | +} |
| 184 | +a[j+r]=t; |
| 185 | +} |
| 186 | +} |
| 187 | +} |
| 188 | +``` |
| 189 | +在程序中使用了三重循环嵌套。最外层的循环用来分解数组元素为多个序列,每次比较两数的间距,直到其值为0就结束循环。下面一层循环按设置的间距r,分别比较对应的数组元素。在该循环中使用插入排序法对指定间距的元素进行排序。 |
| 190 | + |
| 191 | +完整示例代码:[shell.java](./shell.java) |
| 192 | +## 合并排序 |
| 193 | +合并排序(Merge Sort)算法就是将多个有序数据表合并成一个有序数据表。如果参与合并的只有两个有序表,则称为二路合并。对于一个原始的待排序序列,往往可以通过分割的方法来归结为多路合并排序。 |
| 194 | + |
| 195 | +一个待排序的原始数据序列进行合并排序的基本思路是,首先将含有n个结点的待排序数据序列看作由n个长度为1的有序子表组成,将其依次两两合并,得到长度为4的若干有序子表······,重复上述过程,一直到最后的子表长度为n,从而完成排序过 |
| 196 | + |
0 commit comments