一、概述
排序算法是学习编程语言基础过程中躲不开的内容,其实也是很有趣的部分。虽然实际应用还没使用过,但是了解算法的来源以及编码过程对自身逻辑能力的提高非常有帮助。
二、代码
-
直接插入
关键字:认为前面的是已经排好序的。O(n),O(n^2)
public void directInsert(int[] a){
for(int i = 0; i < a.length; i++){
for(int j = i; j>0; j--){
if(a[j]<=a[j-1]){
int temp = a[j-1];
a[j-1] = j[j];
a[j] = temp;
}else{
break;
}
}
}
}
-
直接选择
关键字:选择最小值,放在最前面,记录下标索引O(n2),O(n2)
public void directSelect(int[] a){
for(int i = 0; i < a.length; i++){
int current = i;
for(int j = i+1; j < a.length; j++){
if(a[current]<a[j]){
current = j;
}
if(current != i){
int temp = a[i];
a[i] = a[current];
a[current] = temp;
}
}
}
}
-
冒泡排序
关键字:选出最大的 O(n) O(n^2)
public void bubble(int[] a){
for(int i = 0; i < a.length; i++){
for(int j = 0; j < a.length-1; j++){
if(a[j]>=a[j+1]){
int temp = a[j+1];
a[j+1] = a[j];
a[j] = temp;
}
}
}
}
-
快速排序
- 不知道为什么叫做快速排序,我们只能记住他的核心思想是,选定一个基准,分割数据。其实我认为他也是分治思想。他的指针是 头 和 尾, 归并排序他的指针是 头 和 mid+1,所以他们的退出条件不一样,快速排序是,头要小于尾,归并排序是low小于mid,mid+1要小于high
- 值得注意的是,要先比较后面的(高位),我也不知道为什么
- 退出递归的条件要能想到
关键字:low high 基准 O(nlogn) O(n^2)
public void quickSort(int[] a, int start, int end){
//退出递归条件 ---- 这个要想到
if(start < end) {
int value = a[start];
int low = start;
int high = end;
while(low < high) {
while(low < high && a[high] >= value) { // 这个要放在前面,不然会漏掉元素。
high--;
}
a[low] = a[high];
while(low < high && a[low] <= value) {
low++;
}
a[high] = a[low];
}
a[low] = value;
quickSort(a, start, low); // start 到 low
quickSort(a, low+1,end);
}
}
-
归并排序
-
是分治思想的体现,分为两半,前面一半为start—mid,后面为mid+1 ---- end,有两个指针,分别指向前后面的第一个数字,然后分别比较,小的一个会进入到一个临时数组中。
-
写关键代码的时候,只要心中想着最后一个情况就行,不要想着只有1个或者两个数字的情况,否则比较难相通。
-
注意while()中的判断条件,已经最后要把temp临时数组全部放入对应的原数组中。
-
关键字:low middle high
时间复杂度 n log n
-
public void mergeSort(int[] a, int start, int end){
int mid = (end - start) / 2;
if(start<end){
mergeSort(a,start,mid);
mergeSort(a,mid+1,end);
merge(a,start,mid,end);
}
}
public void merge(int[] a, int start, int mid, int end){
int[] temp = new int[end-start+1];
int index = 0;
int hh = mid + 1;
int i = start;
while(i<=mid && hh<=end){
if(a[i]<=a[hh]){
temp[index] = a[i];
i++;
}else{
temp[index] = a[hh];
hh++;
}
index++;
}
while(hh<=end){
temp[index] = a[hh];
hh++;
index++;
}
while(i<=mid){
temp[index] = a[i];
i++;
index++;
}
for(int k = 0;k< temp.length; k++){
a[k+start] = temp[k];
}
}
-
堆排序
- 首先一个数组可以变成一个完全二叉树,什么是完全二叉树,就是除了最后一层可能是不完全的,其他层都是完全的。
- 那么数组怎么变成一个完全二叉树呢?其实就是单纯的从上倒下,从左到右的把数组的数字放入二叉树中。
- 因此也会有一些表达式:比如如果一个节点对应数组第index位置,那么他的左节点为2*index+1,右节点为2×index+2
- 另外就是什么是大顶堆,也就是根节点大于左右节点。
- 其实构建一个大顶堆代码很简单,两个步骤,构建大顶堆,交换首尾节点,构建大顶堆—
- 关键在于几个点:一个是关键代码有一个递归,因为把最大的数字移动到根节点可能破坏已经弄好的树。 一个是构建大顶堆的时候,从index 开始,递减循环。另外一个点是交换完首尾节点之后再次构建大顶堆,这个时候index 为0(还是想不明白);
public static void heapSort(int[] a) { int index = (a.length - 1) / 2; for(int i = index; i >= 0; i--) { getMaxHeap(a, i, a.length); // 这里为length是因为getMaxHeap 里面是< size } for(int k = a.length-1; k >= 0; k--) { // 这里为length-1 我无法描述,你想一下过程能 明白的 int temp = a[k]; a[k] = a[0]; a[0] = temp; getMaxHeap(a, 0, k); } } public static void getMaxHeap(int[] a, int index, int size) { int left = 2 * index + 1; int right = 2 * index + 2; int max = index; if (left < size && a[left] >= a[max]) { max = left; } if (right < size && a[right] >= a[max]) { // right < size 这个容易忘记 max = right; } if (max != index) { // 这其实就是个退出递归的条件,因此不需要在外面加上 index < size int temp = a[index]; a[index] = a[max]; a[max] = temp; getMaxHeap(a, max, size); } }
- 桶排序
桶排序还是很重要的,典型的空间换时间,对于海量数据,结合位图有时候效果更好,同样的如果不在乎内存大小,用桶排也是ok的,他的时间复杂度就是O(N),稳定性取决于每个桶中的排序算法。
另外一些算法题中,使用桶排有时候也能非常快速的求得答案。
具体思想就是把这些数字按照一定的规律放到若干个数组中,这个规律让他们尽量平均分布,然后每个数组中的数字单独排序,之后再拼起来。
一般桶的数量是: 先遍历集合,得到最大值,和最小值。然后用下面的公式计算:
L e n g t h = m a x / 10 − m i n / 10 + 1 Length= max/10 - min/10 + 1 Length=max/10−min/10+1然后每个元素放在哪个桶里的规律是:这是以10为间隔的。
f ( x ) = x / 10 − m i n / 10 f(x) = x/10 - min/10 f(x)=x/10−min/10public void method(int[] arr) { int max = 0; int min = 0; // 得到最大值 for(int i=0; i < arr.length; i++){ max = Math.max(max, arr[i]); min = Math.min(min, arr[i]); } // 创建桶 int bucketNum = max / 10 - min /10 + 1; ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum); for(int j = 0; j < bucketNum; j++){ bucketArr.add(new ArrayList<Integer>()); } // 根据映射关系放入对应的桶内 for(int i=0; i < arr.length; i++){ int index = arr[i] / 10 - min/10; bucketArr.get(index).add(arr[i]); } // 对每个桶进行排序 for(int i = 0; i < bucketArr.size(); i++){ Collections.sort(bucketArr.get(i)); } // 把排序号的结果返回a中 int index = 0; for(int i = 0; i < bucketArr.size(); i++){ for(int j = 0; j < bucketArr.get(i).size(); j++){ arr[index++] = bucketArr.get(i).get(j); } } }