基础排序算法总结(插入、选择、冒泡、合并、二分查找、堆排序、快速排序、基数排序、桶排序、计数排序)

      本文是在学习《算法导论》的基础排序算法过程中,自己写的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

 

 

3Java实现

    

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 stub
		InsertionSort 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

 

3Java实现

     

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, pqr)

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(Apr)

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

 

                   3Java代码

                         

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个输入元素中每一个都是0k之间的证书。

         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中每一个元素都是在0k之间,所以可以把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,所以必须要减去1
			outputArray[tempArray[inputArray[p]] -1] = inputArray[p];
//			tempArrayp--;  这里是要对临时数组里面的数值减少1,所以不能用临时变量,注意点。
			tempArray[inputArray[p]] -= 1;
		}
		
		return outputArray;
	}
}


 

        

         八基数排序

                   基数排序首先按照最低有效位数字进行排序。

                   1Java代码

                     

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:算法思想

当桶排序的输入符合均匀分布时,把区间[01)划分成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);
				}
			}
		}*/
	}
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值