基础排序算法总结(插入、选择、冒泡、合并、二分查找、堆排序、快速排序、基数排序、桶排序、计数排序)
来源:互联网 发布:食品标签制作软件下载 编辑:程序博客网 时间:2024/05/28 15:07
本文是在学习《算法导论》的基础排序算法过程中,自己写的Java代码,深入掌握思想,并且有能力实现,并且分析其复杂度。本文对于每一个算法,按照算法思想、伪代码、Java实现、复杂度简单分析的方式分析每一个排序。
一 插入排序
1:算法思想
插入排序是对少量元素进行排序的有效算法。扑克牌摸牌时就是典型的插入排序,无论在什么时候,左手中的牌都是排好序的。
2:伪代码
INSERTION-SORT(A)
1 for j <-- 2 tolength[A]
2 do key < A[j]
3 //Insert A[j] into the sorted sequenceA[1..j-1]
4 I <-- j – 1
5 While I > 0 and A[i] > key
6 Do A[i+1] <-- A[i]
7 I <-- I – 1
8 A[I+1] <-- key
3:Java实现
package chapter1;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;public class InsertionSort {/** * @param args * @throws IOException */public static void main(String[] args) throws IOException {// TODO Auto-generated method stubInsertionSort is = new InsertionSort();int[] intArray = is.readFromConsole();is.insertionSort(intArray);for(int ie : intArray) {System.out.print(ie);}}/** * read from the console, read the input number as integer. * @return * @throws IOException */private int[] readFromConsole() throws IOException {BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));String in = stdIn.readLine();int[] intArray = null; if(null == in || "".equals(in)) {System.out.println("Please enter the array you want to sort");System.exit(0);} else{String[] strArray = in.split(",");intArray = new int[strArray.length];for(int i = 0; i < strArray.length; i++) {intArray[i] = Integer.parseInt(strArray[i].trim());}}return intArray;}/** * 帮助理解: * 1:j表示新摸到手中的第j张牌,key表示这张新牌的值,想要把这张牌插入到 * 手中已经排好序的牌里,从右往左,依次比较。所以j从1开始。 * 2:i表示手中右边第一张牌,和新牌比较大小,大的话就往右移动一个位置, * 这样,手中从右边看,第一张牌和第二张牌之间有了一个空隙,然后i自减,比较第右边第二张牌…… * 依次执行,直到某一张牌不大于新摸牌,这时在循环里i已经自减了,所以要把牌插在第i张牌的下一个位置 * 也就是i+1上。 * insertion sort * @param intArray * @return */private int[] insertionSort(int[] intArray) {for(int j = 1; j < intArray.length; j++) {int key = intArray[j];int i = j - 1;while(i >= 0 && intArray[i] > key) {intArray[i+1] = intArray[i];i--;}intArray[i+1] = key;}return intArray;}}
4:复杂度 O(n2)
最坏情况:当输入数组是按照逆序排序的时候,每个元素都必须与整个已排序的数组每一个元素进行比较。
二选择排序
1:算法思想
对数组A中的n个数进行排序,首先找出A的最小元素,并与A[1]交换,接着找出A中的第二小元素,与A[2]中的元素交换,对A中头n-1个元素继续这一过程,称为选择排序。
2:伪代码
SELECTION-SORT
1 for i<-- 1 ton-1
2 min <-- A[i]
3 For j<-- i+1 ton-1
4 If A[j] < min
5 M<--A[j]
6 A[j] <-- A[i]
7 A[i] <-- min
3:Java实现
package chapter1;public class SelectionSort {public static void main(String[] args) {int[] arrays = new int[]{3, 2, 7, 1};int[] sortedArrays = select(arrays);for(int i : sortedArrays) {System.out.println(i);}}/** * -----每次选取最小的------- * * 注意点: * 1:对于i以及j的值的选取 * 2:最后if语句中,只有当找到比当前min值还小的值才互换值。arrays[j] = arrays[i]; arrays[i] = min;这两句 * 应当放到if语句里面 * *帮助理解: *1:两层循环,外层i表示每一次的遍历,每一次遍历找到最小的恰好可以放到第i个位置上。 * @param arrays * @return */ private static int[] select(int[] arrays) { for(int i = 0; i < arrays.length - 1; i++) { int min = arrays[i]; for(int j = i + 1; j <= arrays.length - 1; j++) { if(arrays[j] < min) { min = arrays[j]; arrays[j] = arrays[i]; arrays[i] = min; } } }return arrays;}}
4:复杂度O(n2)
三冒泡排序
1:算法思想
重复的交换相邻的两个反序元素。
2:伪代码
BUBBLESORT(A)
1 for I <-- 1 tolength[A]
2 do for j<-- length[A] downto i+1
3 Do if A[i] < A[j – 1]
4 Then exchange A[j] <--> A[j-1]
3:Java代码
package chapter1;public class BubbleSort {public static void main(String[] args) {int[] arrays = new int[] {8, 3, 5, 1};for(int i : bubbleSort(arrays)) {System.out.println(i);}}/** * -----两两相邻的移动互换—————— * 这种方式是把最小的元素冒泡到最前面。 * 这里的i和选择排序中的一样,每一次循环把最小值直接放到i位置上。 * @param arrays * @return */private static int[] bubbleSort(int[] arrays) {for(int i = 0; i < arrays.length; i++) {for(int j = arrays.length - 1; j > i; j--) {//里面是很简单的swap函数。if(arrays[j] < arrays[j-1]) {int temp = arrays[j];arrays[j] = arrays[j-1];arrays[j-1] = temp;}}}return arrays;}}
4:时间复杂度O(n2)
四合并排序
1:算法思想
分解:将n个元素分成各含n/2个元素的子序列
解决:用合并排序法对两个子序列递归的排序
合并:合并两个已排序的子序列得到排序结果
2:伪代码
一个辅助过程MERGE(A, p, q,r)。该过程假设子数组A[p…q]和A[q+1…r]都已经排好序,并将它们合并成一个已排好序的子数组代替当前子数组A[p….r]。
很简单,就是把整个数组分为两个数组,一个数组全部拷贝A[p…q],另外一个全部拷贝A[q+1…r]。然后对这两个数组每一个设置指针比较。下面的伪代码为了避免每一步中都检查数组是不是空的,在每一个底部放上“哨兵牌”。故下面3中把长度都加1。
MERGE(A, p,q,r)
1 length1 = q-p+1
2 length2 = r-q
3 create arrays L[1…length1+1]and R[1…lenght2 +1]
4 for I <-- 1 tolength1
5 do L[i]<--A[p+i-1]
6 For j<-- 1 tolength2
7 Do R[j]<--A[q+j]
8 L[length1 +1]<--∞
9 R[length2 +1]<--∞
10 I = 1
11 J=1
12 For k <-- p to r
13 Do if L[i] <= R[j]
14 Then A[k]<-- L[i]
15 I<--i+1
16 Else A[k]<--R[j]
17 J<--j+1
MERGE-SORT(A, p,r)
1 if p<r
2 then q<--(p+r)/2
3 MERGE-SORT(A, p, q)
4 MERGE-SORT(A, q+1, r)
5 MERGE(A, p, q, r)
3: Java代码
package chapter1;public class MergeSort {public static void main(String[] args) {int[] tests = new int[]{3, 2, 9, 4, 1, 7, 8};mergeSort(tests, 0, tests.length - 1);for(int test : tests) {System.out.println(test);}} public static void mergeSort(int[] arrays, int begin, int end) { if(begin >= end) { return; } else { int mid = begin + (end - begin)/2; mergeSort(arrays, begin, mid); mergeSort(arrays, mid + 1, end); merge(arrays, begin, mid, end); } } /** * 理解:这里和算法导论上不同的是,并没有使用两个中间数组,直接使用原来的数组, * 但是可以用两个不同的下标实现。 * 只有一个arraysB数组是为了存储排序好的结果。k是arrayB数组的下标。 * @param arraysA * @param begin * @param mid * @param end */ private static void merge(int[] arraysA, int begin, int mid, int end) { int[] arraysB = new int[end - begin + 1]; int leftBegin = begin; int rightBegin = mid + 1; int k = 0; while(leftBegin <= mid && rightBegin <= end ) { if(arraysA[leftBegin] <= arraysA[rightBegin]) { arraysB[k] = arraysA[leftBegin]; leftBegin++; } else { arraysB[k] = arraysA[rightBegin]; rightBegin++; } k++; } //下面判断有一方数组先没有值了,那么直接把另外一方的值全部放到后面就可以。注意这里不要用成了if. //和上面while语句中的表达式对比,执行完while语句后, //左边和右边只可能有一方剩下多个,可以直接放到后面,也就是说剩下的那一方肯定还是满足while语句中的条件 while(leftBegin <= mid) { arraysB[k++] = arraysA[leftBegin++]; } while(rightBegin <= end) { arraysB[k++] = arraysA[rightBegin++]; } for(int j = 0; j < arraysB.length; j++) { arraysA[begin + j] = arraysB[j]; }}}
4:时间复杂度O(n)
五快速排序
1:算法思想
划分数组,就地排序。
2:伪代码
QUICKSORT(A,p,r)
1 if p<r
2 then q<--PARTITION(A,p, r)
3 QUICKSORT(A, p, q-1)
4 QUICKSORT(A, q+1, r)
PARTITION(A, p,r)
1 x<--A[r]
2 i<--p-1
3 for j<-- p tor-1
4 do if A[j]<=x
5 then i<--I+1
6 exchange A[i] <-->A[j]
7 exchange A[i+1] <-->A[r]
8 return i+1
3:Java代码
package chapter1;public class QuickSort {public static void main(String[] args) {int[] tests = new int[]{4, 2, 6, 9, 1, 0, 5};quickSort(tests, 0, tests.length - 1);for(int test : tests) {System.out.println(test);}}private static void quickSort(int[] arrays, int p, int r) {if(p < r) {int pivot = partition(arrays, p, r);quickSort(arrays, p, pivot -1);quickSort(arrays, pivot + 1, r);}}//这里的找pivot的方法很好,算法导论给出的方法非常好//自己简单的画数组图很容易明白。j的值到r-1,for循环结束后再把pivot值放到应该放的地方//也就是说i位置的下一位。private static int partition(int[] arrays, int p, int r) {int temp = arrays[r];//pivot是最右边的那个值int i = p - 1;//i永远指向左边数组的最高位置,初值为p左边那个下标。for(int j = p; j <= r - 1; j++) {if(arrays[j] < temp) {i++;//这里注意,要先把i自增让后才交换。因为i表示的小于pivot值的数组的最右边的下标。swap(arrays, i, j);}}swap(arrays, i + 1, r);return i+1;}private static void swap(int[] arrays, int i, int j) {int temp = arrays[i];arrays[i] = arrays[j];arrays[j] = temp;}}
4:时间复杂度O(n2),平均为O(nlg n)
六堆排序
1:算法思想
用到三个方法,一个是保持对性质的MAX-HEAPIFY方法,这个方法保持最大堆的性质,也就是每一个节点都大于左右孩子小于父节点。
建堆方法:BUILD-MAX-HEAP(A)方法,把一个数组构建惩最大堆,不断的调用MAX-HEAPIFY方法。并且,有一个特点,可以直接从数组的中间处开始,因为数组中后面 的一半都是树中的叶子。
然后是堆排序方法,首先对数组调用建堆方法,然后对数组中的每一个元素,不断调用MAX-HEAPIFY方法。
2:伪代码
假设以LEFT(i)和RIGHT(i)为根的两颗二叉树都是最大堆。
MAX-HEAPIFY(A,i)
1 l<--LEFT(I)
2 r<--RIGHT(I)
3 if l <= heap-size[A] andA[l]>A[i]
4 then largest<-- l
5 Else largest<-- i
6 If r<= heap-size[A] andA[r]>A[largest]
7 Then largest<--r
8 If largest != i
9 Then exchange A[i]<-->A[largest]
10 MAX-HEAPIFY(A, largest)
BUILD-MAX-HEAP(A)
1 heap-size[A]<--length[A]
2 for i<--length[A]/2 downto 1
3 do MAX-HEAPIFY(A, i)
MEAPSORT(A)
1 BUILD-MAX-HEAP(A)
2 For i<--length[A]downto 2
3 Do exchange A[1]<-->A[i]
4 Heap-size[A]<--heap-size[A] -1
5 MAX-HEAPIFY(A, 1)
3 java code
package chapter1;/** * 假设array数组整个用来建堆 * 1:堆的根节点属性是1,而非数组中的0,所以,这里所有的都是应用的二叉树上的数,也就是从1开始的,只有对数组操作的时候才在相应的减1 * 2:别忘记参数heapSize的使用。 * @author zhaoxin * */public class HeapSort {public static void main(String[] args) {int[] tests = {3, 2, 9, 8, 3, 5, 7};heapSort(tests);printFunction(tests);}/** * 注意点:因为构建堆之后第一次交换已经把一个最大的放到数组最后了, * 所以这里直接使用i值作为堆的大小。 * @param array */private static void heapSort(int[] array) {buildMaxHeap(array);for(int i = array.length - 1; i > 0; i--) {swap(array, 0, i);//每次把堆顶元素与堆最后一个元素置换,然后堆大小减去1,然后对堆顶调用保持最大堆性质的方法。maxHeapify(array, 1, i);//这里注意点,一直是在数组最开头不断的调用这个方法。}}/** * 注意点:建堆的时候,从数组中下表为(array.length/2 - 1)到0的是非叶子节点 * 但是因为maxHeapify的中间参数对应着堆中逻辑结构的节点,所以root要比数组中的都大1 * @param array */private static void buildMaxHeap(int[] array) {for(int root = array.length / 2; root >= 1; root--) {maxHeapify(array, root, array.length);}}/** * 注意点:在于中间int参数都是对于堆的逻辑结构来说的,正好比数组中的大1,所以在操作数组的时候要减去1 * 因为数组从0开始,但是堆却是从1开始。 * @param array * @param rootIndex * @param heapSize */private static void maxHeapify(int[] array, int rootIndex, int heapSize) {int leftChildIndex = rootIndex * 2;int rightChildIndex = rootIndex * 2 + 1;int largestIndex = rootIndex;if(leftChildIndex <= heapSize && array[leftChildIndex - 1] > array[rootIndex - 1]) {largestIndex = leftChildIndex;} if(rightChildIndex <= heapSize && array[rightChildIndex -1] > array[largestIndex - 1]) {largestIndex = rightChildIndex;}if(largestIndex != rootIndex) {swap(array, rootIndex - 1, largestIndex -1);maxHeapify(array, largestIndex,heapSize);}}private static void swap(int[] array, int i, int j) {int temp = array[i];array[i] = array[j];array[j] = temp;}private static void printFunction(int[] array) {for(int test : array) {System.out.println(test);}}}
七计数排序
计数排序假设n个输入元素中每一个都是0到k之间的证书。
1:算法思想
对每一个输入元素x,确定出小于x的元素个数,有了小于x的元素的个数,也就知道x第几大,就可以把x直接放到它最终输出数组中的位置上。
2:伪代码 A[1..n]表示输入数组, B[1..n]存储排序结果,C[o…k]提供临时存储区。
COUNTING-SORT(A, B, k)
1 for I <-- 0 to k
2 do C[i]<--0
3 For j<--1 tolength[A]
4 Do C[A[j]]<--C[A[j]] + 1
5 //因为数组A中每一个元素都是在0到k之间,所以可以把A中值直接放到c中当作下表使用。现在C[i]包含A中等于i的元素个数
6 For I<--1 to k
7 Do C[i]<--C[i] +C[i-1]
8 //这个时候,数组c[i]里面包含小于或者等于i的元素个数
9 For j<--length[A[ downto 1
10 Do B[C[A[j]]]<--A[j]
11 C[A[j]]<--C[A[j]]-1
3:Java代码
package chapter1;public class CountSort {public static void main(String[] args) {int[] tests = new int[] {2, 1, 2, 5, 9, 3, 2, 7};int[] results = countSort(tests, 9);System.out.println();for(int result : results) {System.out.println(result);}}/** * 注意点 * 1:tempArray的元素个数是k+1 * 2:对于32行,一个经验点,想要对数组的某一个操作赋值,不能用变量去替换。 * @param inputArray * @param k 为输入数组中最大的数 * @return */private static int[] countSort(int[] inputArray, int k) {int[] tempArray = new int[k+1];int[] outputArray = new int[inputArray.length];for(int i = 0; i < inputArray.length; i++) {tempArray[inputArray[i]] += 1;}for(int j = 1; j <= k; j++) {tempArray[j] += tempArray[j-1];}for(int p = inputArray.length - 1; p >= 0; p--) {//下面之所以要减去1,是因为outputArray大小和inputArray一样,当时tempArray的值最大是inputArray的长度length,所以必须要减去1outputArray[tempArray[inputArray[p]] -1] = inputArray[p];//tempArrayp--; 这里是要对临时数组里面的数值减少1,所以不能用临时变量,注意点。tempArray[inputArray[p]] -= 1;}return outputArray;}}
八基数排序
基数排序首先按照最低有效位数字进行排序。
1:Java代码
package chapter1;public class RadixSort {public static void main(String[] args) {int[] inputArray = new int[]{371, 42, 470};int[] resultArray = radixSort(inputArray, 3);for(int i : resultArray) {System.out.println(i);}}/** * 完全就是在计数排序外面包了一个for循环,来对于每一位上使用计数排序. * 1:k表示第几位 * 2:计算每一位上的数值的方法 * 3: * @param inputArray * @param digit * @return */private static int[] radixSort(int[] inputArray, int digit) {for(int k = 1; k <= digit; k++) {int[] tempRadixSortArray = new int[inputArray.length];int[] tempCountingSortArray = new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0};for(int i = 0; i < inputArray.length; i++) {//下面是取每一项在第k位的值,然后应用计数排序int tempDigit = inputArray[i]/(int)Math.pow(10, k-1) - (inputArray[i]/(int)Math.pow(10, k))*10;tempCountingSortArray[tempDigit] += 1;}for(int j = 1; j < 10; j++) {tempCountingSortArray[j] += tempCountingSortArray[j-1];}for(int n = inputArray.length -1; n >= 0; n--) {//下面和普通的计数排序的区别在于使用了tempDigit去代替原来直接使用n,这是为了和输入数组中的值关联起来。int tempDigit = inputArray[n]/(int)Math.pow(10, k-1) - (inputArray[n]/(int)Math.pow(10, k))*10;tempRadixSortArray[tempCountingSortArray[tempDigit] - 1] = inputArray[n];tempCountingSortArray[tempDigit] -= 1;}//下面直接在原来输入数组赋值,可以节省数组空间。for(int p = 0; p < inputArray.length; p++) {inputArray[p] = tempRadixSortArray[p];}}return inputArray;}}
九桶排序
1:算法思想
当桶排序的输入符合均匀分布时,把区间[0,1)划分成n个相同大小的子区间。将n个输入数分布到各个桶中去,先对桶中的数进行排序,然后按照次序把桶中的元素列出来即可。
2:伪代码
BUCKET-SORT(A)
1 n<--length[A]
2 for i<--1 to n
3 do insert A[i] into list B[nA[i]]
4 For i<--0 ton-1
5 Do sort list B[i] with insertion sort
6 Concatenate the lists B[0],B[1], …in order
3: java code
package chapter1;import java.util.ArrayList;import java.util.Iterator;/** * 桶排序:桶排序的思想是把区间[0,1)划分成n个相同大小的子区间,称为桶,然后将n个输入数分布到各个桶中去。 * 因为输入数均匀且独立分布在[0,1)上,所以,一般不会有很多数落在一个桶中的情况。 * 为了得到结果,先对各个桶中的数进行排序,然后按次序把各桶中的元素列出来。 * 桶排序的时间复杂度为O(n) */public class BucketSort {public static void main(String[] args) {double arr[] = {0.78,0.17,0.39,0.26,0.72,0.94,0.21,0.12,0.23,0.68};bucketSort(arr);for(int i = 0;i<arr.length;i++)System.out.println(arr[i]);}private static void bucketSort(double[] inputArray) {int n = inputArray.length;//bucketLists是用来存放桶,桶里面装的是链表ArrayList[] bucketLists = new ArrayList[n];for(int i = 0; i < n; i++) {int temp = (int)Math.floor(n*inputArray[i]);if(null == bucketLists[temp]) {bucketLists[temp] = new ArrayList();}bucketLists[temp].add(inputArray[i]);}for(int j = 0; j < n; j++) {if(null != bucketLists[j])insertionSort(bucketLists[j]);}int index = 0;for(int i = 0; i < n; i ++) {if(null != bucketLists[i]) {Iterator iterator = bucketLists[i].iterator();while(iterator.hasNext()) {inputArray[index++] = (Double) iterator.next();}}}}private static void insertionSort(ArrayList list) {for(int i = 1; i < list.size(); i++) {double key = (Double)list.get(i);int j = i - 1;while(j >= 0 && (Double)list.get(j) > key) {list.set(j + 1, list.get(j));j--;}list.set(j + 1, key);}/*if(list.size()>1){for(int i =1;i<list.size();i++){if((Double)list.get(i)<(Double)list.get(i-1)){double temp = (Double) list.get(i);int j = i-1;for(;j>=0&&((Double)list.get(j)>(Double)list.get(j+1));j--)list.set(j+1, list.get(j));list.set(j+1, temp);}}}*/}}
- 基础排序算法总结(插入、选择、冒泡、合并、二分查找、堆排序、快速排序、基数排序、桶排序、计数排序)
- 排序算法(插入排序、shell排序、冒泡排序、选择排序、合并排序、堆排序、快速排序、计数排序、基数排序、桶排序)
- 排序算法----冒泡排序+插入排序+选择排序+快速排序+希尔排序+堆排序+归并排序+计数排序+基数排序+桶排序(c语言)
- 【更新】排序算法比较:插入排序,冒泡排序,归并排序,堆排序,快速排序,计数排序,基数排序,桶排序
- 各种排序算法总结----基数排序、归并排序、插入排序、冒泡排序、选择排序、快速排序、堆排序、希尔排序
- 经典排序算法设计与分析(插入排序、冒泡排序、选择排序、shell排序、快速排序、堆排序、分配排序、基数排序、桶排序、归并排序)
- 经典排序算法设计与分析(插入排序、冒泡排序、选择排序、shell排序、快速排序、堆排序、分配排序、基数排序、桶排序、归并排序)
- 数组排序 —— 常用基础数组排序算法(冒泡、选择、插入、归并、快速、堆、希尔、计数、基数排序)
- 排序算法复习(Java实现):插入,冒泡,选择,Shell,快速排序, 归并排序,堆排序,桶式排序,基数排序
- 排序算法---基础算法(冒泡排序,快速排序,选择排序,直接插入排序,桶排序)
- C# 插入排序 冒泡排序 选择排序 快速排序 堆排序 归并排序 基数排序 希尔排序
- 各种常见的排序,冒泡排序,选择排序,插入排序,希尔排序,堆排序,快速排序,基数排序,桶排序
- 基本算法简单实现-二分法查找、合并排序、冒泡排序、插入排序、选择排序、快速排序
- 笔试面试最常涉及到的12种排序算法(包括插入排序、二分插入排序、希尔排序、选择排序、冒泡排序、鸡尾酒排序、快速排序、堆排序、归并排序、桶排序、计数排序和基数排序)进行了详解。每一种算法都有基本介绍、算
- 快速排序 选择排序 插入排序 冒泡排序 堆排序 归并排序 基数排序
- 十种排序算法总结(冒泡、插入、选择、希尔、归并、堆、快速,计数,桶,基数)
- 十种排序算法总结(冒泡、插入、选择、希尔、归并、堆、快速,计数,桶,基数)
- 十种排序算法总结(冒泡、插入、选择、希尔、归并、堆、快速,计数,桶,基数)
- .Net中有关批量添加
- 黑马程序员_学习日记20_ASP.Net初级
- 真机 解决方法 unavailable shared library com.google.android.maps
- Windows下的Win32串口编程
- Deteriorate, Degrade, Descend, Decay
- 基础排序算法总结(插入、选择、冒泡、合并、二分查找、堆排序、快速排序、基数排序、桶排序、计数排序)
- networkx使用笔记(三)之好汉篇numpy(2)
- jquery之超简单的div显示和隐藏特效demo
- linux0.11学习1之bootsect
- Oracle 函数大全(字符串函数,数学函数,日期函数,逻辑运算函数,其他函数)
- Linux IO子系统和文件系统读写流程
- .NET中使用ODP(Oracle.DataAccess)连接Oracle数据时出现的异常
- S3C2440驱动4.3寸TFT屏程序
- 临时对象作为函数返回值返回的分析