算法總結之十種內部排序算法

一、1、首先推薦一個算法的可視化的一個網站:https://www.cs.usfca.edu/~galles/visualization/Algorithms.html.

二、O(n*n)--雙循環

1、冒泡排序--兩兩對換

package Sort;

public class BubbleSort {
	public static void BubbleSort(int a[]){
		int len = a.length;
		/*外循環的目的是:
			考慮到最壞的情況即:
				如果整個數組全部倒序的話
					len個元素,要比較len-1次	
		*/
		for(int i = 0;i < len;i++) {
			/*內循環目的是:
				考慮最壞的情況
					如果數組的第n個元素是第n大的元素,那麼從第n個元素一直交換到倒數第n個需要        
                    len-1 - j 次 
			*/
			for(int j = 0;j < len - 1;j++){
				if(a[j+1] < a[j]){
					int temp = a[j];
					a[j] = a[j+1];
					a[j+1] = temp;	
				}
			}
		}
	}
	//改進BubbleSort
	/*
	考慮到什麼時候bubbleSort停止了排序
		當數組中的元素的順序從小到大時候-->內循環的 if(a[j+1] < a[j]) -->目的是遍歷數組中所有 
         相鄰兩個是否符合順序
		-->因爲只要數組元素不符合順序的話-->遍歷數組中的所有相鄰兩個元素一定存在不符合順序相鄰兩個元素-->反之如果
		數組中所有相鄰兩個之間均符合順序的話-->就可以得到整個數組是順序的
	*/
	public static void BubbleSort_1(int a[]){
		int len = a.length;
		/*外循環的目的是:
			考慮到最壞的情況即:
				如果整個數組全部倒序的話
					len個元素,要比較len-1次	
		*/
		for(int i = 0;i < len;i++) {
			/*內循環目的是:
				考慮最壞的情況
					如果數組的第n個元素是第n大的元素,那麼從第n個元素一直交換到倒數第n個需要len-1 - j 次 
			*/
			boolean flag = true;
			for(int j = 0;j < len - 1;j++){
				if(a[j+1] < a[j]){//這裏寫等於小於就會導致算法的不穩定//不穩定沒排序之前的 ai 在 aj前面(ai <= aj),
					int temp = a[j];
					a[j] = a[j+1];
					a[j+1] = temp;
					flag = false;	
				}
			}
			if(flag)
				break;
		}
	}
	public static void main(String[] args) {
		int a[] = RandomArray.Array(10,1,10);//自己創建的方法用來生成隨機數組
		for(int i:a)
			System.out.print(i+" ");
		BubbleSort(a);
		System.out.println("");
		for(int i:a)
			System.out.print(i+" ");
		
		/*System.out.println("");
		a = RandomArray.Array(10);
		BubbleSort_1(a);
		for(int i:a)
			System.out.print(i+" ");
		 */
	}

}

2、插入排序--選一插入

package Sort;
/**
 * 2019/3/10
 * @author Administrator
 */
/*
 * 算法描述:
 * 	指針首先指向第一個元素,然後指向第一個元素右面的元素,如果指針指向的元素比第一個元素小就移動到第一個元素的左面
 * 	如此心來的元素移動到最後一個比其大的元素的右面
 *  若比第一個元素大,不動,指針向右指到下一個元素
 */
public class InsertSort {
	//10 5 4 5 3 1 10 8  3 8 
	// 5 10 4 5 3 1 10 8 3 8
	// 
	public static void insertSort(int a[]){
		int len = a.length;
		/*
		 * 外循環的目的:
		 * 	逐個將第i個元素插入到前 i 個元素中
		 */
		for(int i = 1;i < len;i++){
			int p = i - 1;
			int insertElement = a[i];
			/*	while循環的目的:
			 * 		插入第i個元素的時候,
			 * 		兩兩進行比較如果第i個元素的值小於
			 * 		第i個元素值前面的元素就交換第i個元素值與第p個元素的值
			 * 		一直到第i個元素的值大於其前面的元素的值爲止或者到達第一個元素爲止 
			 */
			while(p >= 0&&insertElement < a[p]){
				int temp = a[p];
				a[p] = insertElement;
				a[p+1] = temp;
				p--;
			}
		}
	}
	
	public static void main(String[] args) {
		int a[] = RandomArray.Array(10);
		
		for(int i:a)
			System.out.print(i+" ");
		insertSort(a);
		System.out.println("");
		for(int i:a)
			System.out.print(i+" ");
	}

}

3、希爾排序--分組的插入排序

package Sort;

public class ShellSort {
/*shellSort是對InsertSort的一種改進算法
 * 思想是:
 * 	改良的出發點是減少while語句的使用和減少比較的次數
 * 	操作:
 * 		分組
 * 		插入
 * 		分組
 */
	public static void shellSort(int a[],int gap){
		int len = a.length;
		
		for(int i = gap;i < len;i = i + gap){
			int p = i - gap;
			int element = a[i];
			
			while(p >= 0 && a[p] > element){
				int temp = a[p];
				a[p] = element;
				a[p+gap] = temp;
				p = p - gap;
			}
		}
	}
	
	public static void shellSort(int a[]){
		shellSort(a,5);
		shellSort(a,2);
		shellSort(a,1);
	}
	
	public static void main(String[] args) {
		int a[] = RandomArray.Array(10);
		
		for(int i:a)
			System.out.print(i+" ");
		shellSort(a);
		System.out.println("");
		for(int i:a)
			System.out.print(i+" ");
	}

}

4、選擇排序--逐步選取

package Sort;

public class SellectionSort {
	/*
	 * 選擇排序與插入排序有些類似不同的是
	 * 	插入排序是將第i個元素插入到前i個元素中間
	 * 	選擇排序是從第i到第len-1個元素中選取一個最小的元素
	 * 	與第i個元素進行交換
	 */
	public static void sellectionSort(int a[]){
		int len = a.length;
		/*外循環的目的:
		 * 	從第i個元素之前的元素都是已經排序好的
			含義就相等於 當i = 0 的時候找最小的元素
						  i = 1 的時候找次小的元素
						  i = 2 的時候找次次小的元素
						  以此類推
		 */
		
		for(int i = 0;i < len;i++){
			int minIndex = i;
			
			/*
			 * 內循環的目的:
			 * 	尋找第i+1個到第len-1個元素中最小的元素的index
			 */
			for(int j = i + 1;j < len;j++){
				if(a[j] < a[minIndex]){
					minIndex = j;	
				}
			}
			
			/*
			 * 這一步交換第i+1個到第len-1個元素中最小的元素與第i個元素
			 */
			
			int temp = a[minIndex];
			a[minIndex] = a[i];
			a[i] = temp;
		
			}
	}
	
	
	public static void main(String[] args) {
		int a[] = RandomArray.Array(10,1,10);
		for(int i:a)
			System.out.print(i+" ");
		sellectionSort(a);
		System.out.println("");
		for(int i:a)
			System.out.print(i+" ");
	}

}

三、O(n*logn) --運用到遞歸

1、歸併排序

package Sort;
/*
 * 歸併的思想:
 * 	將數組長度一次又一次的分割成兩部分知道當一組數組的元素小於等於1的時候
 * 	在返回到上一次數組元素大於1但小於4的狀態進行下一個分割,當這次分割成數組
 * 	元素的數量小於等於1的時候在進行合併。(如果對遞歸的這部分不太瞭解可以進行一步一步的debug瞭解遞歸的含義-->程序只有當把一個遞歸語句運行到return的時候纔會進行下一步操作)
 * 	(關於遞歸算法後更)
 * 	因此可得遞歸的作用知識對數組進行等元素分割,真正對數組元素排序的是merge
 */
public class MergeSort {
	
	public static void mergeSort(int a[]){
		if(a.length <= 1){
			return ;
		}
		
		/*
		 * 下面語句的目的是將數組元素分爲兩部分直到
		 * 這倆個部分元素只有一個的時候在return進行merge
		 */
		int len = a.length;  //當前整個數組的長度
		int firstLength = len / 2;	//第一個數組的長度	
		int firstHalf[] = new int[firstLength];//
		System.arraycopy(a,0,firstHalf,0,firstLength); //將當前數組的以第一個元素開始長度爲firstLength的元素全部複製到firstHalf數組內
		mergeSort(firstHalf);//這一部分的數組進入遞歸
					
		//當上一部分的數組遞歸到只有一個元素的時候返回然後進行下面的語句
		int secondLength = len - firstLength;//第二部分的數組的長度等於當前整個數組的長度--第一部分的數組的長度
											//這裏可能會寫成 len / 2 是不對的 因爲如果 len爲奇數那麼兩次整除的值相加便不是len了例如 第一次 3 / 2 = 1 
																															//第二次 3 / 2 = 1 	
		int secondHalf[] = new int[secondLength];//
		System.arraycopy(a,firstLength, secondHalf,0,secondLength);//將當前數組的以第firstLength個元素長度爲secondLength的元素複製到secondHalf中
		mergeSort(secondHalf);//對這一部分的數組進行遞歸
		//當上一部分的數組遞歸到只有一個元素的時候返回然後進行下面的語句
		int temp[] = meger(firstHalf,secondHalf);//對已經得到的兩部分進行merge
		System.arraycopy(temp,0,a,0,temp.length);//對當前這兩部分merge結束之後這一程序結束相當於return當還有遞歸沒有結束的時候再一次運行這個程序
	}
	
	
	public static int[] meger(int b[],int c[]){
		int len1 = b.length;
		int len2 = c.length;
		int temp[] = new int[len1+len2];
		
		int p1 = 0;
		int p2 = 0;
		int p3 = 0;
		
		while(p1 < len1 && p2 < len2){
			if(b[p1] < c[p2]){
				temp[p3++] = b[p1++];
			}else{
				temp[p3++] = c[p2++];
			}
		}
		while(p1 < len1){
			temp[p3++] = b[p1++];
		}
		while(p2 < len2){
			temp[p3++] = c[p2++];
		}
		
		return temp;
	}
	
	public static void main(String[] args) {
		int a[] = RandomArray.Array(10,1,10);
		for(int i:a)
			System.out.print(i+" ");
		mergeSort(a);
		System.out.println("");
		for(int i:a)
			System.out.print(i+" ");

	}

}

2、快速排序

package Sort;
/*
 * quickSort與mergeSort相似
 * 不同之處是:
 * 	quickSort將重心放在如何使得將數組進行劃分使得劃分成的兩部分一部分小於一個數另一部分大於這一個數
 * 	mergeSort目的是將數組劃分後進行合併
 */

	
public class QuickSort {
	/* quickSort提高效率的一個方法就是對主元的選取要準確
	 * 這個方法對主元的選取並不那麼高效
	 * */
	public static int partition(int a[],int b,int e){
		int privot = a[b];
		int p1 = b+1;
		int p2 = e;
		
		while(p1 <= p2){ //這一個while的條件可以寫 p1 <= p2 也可以寫p1 < p2
			if(a[p1] <= privot){    //考慮一下臨界狀態當p1 == p2的時候              
									//p1 == p2 -->這兩個指針指向的元素是相同的-->無論的這個元素大於
								  //主元(p2-- -->p2所指向的元素是小於主元的)或者小於主元p1++(p2指向的元素小於主元)
								  //只要交換p2指向的元素與主元就可以了
				p1++;             
			}else{					//如果寫成p1<p2的話還要再寫判斷p1 p2共同指向的元素是大於主元還是小於主元                
				int temp = a[p1];
				a[p1] = a[p2];
				a[p2] = temp;
				p2--;
			}
		}
		
		int temp = a[p2];
		a[p2] = a[b];
		a[b] = temp;
		
		return p2;
	}
    public static void quickSort(int a[],int b,int e){
		if(b > e)
			return;
		int q = partition(a,b,e);
		quickSort(a,b,q-1);
		quickSort(a,q+1,e);
	}
	
	public static void main(String[] args) {
		int a[] = {3,1,4,5,7,3,4,0,9,2};
		quickSort(a,0,a.length-1);
		for(int i:a)
			System.out.print(i+" ");

	}

}

四、O(n) --類桶排序

1、桶排序--桶是鏈表

package Sort;

import java.util.Collections;
import java.util.LinkedList;

public class BucketSort {
	
	public static void bucketSort(int a[]){
		int buckets = 10;
		LinkedList list[] = new LinkedList[buckets];  //10是桶的個數,可以根據數組元素的數量進行設置
		for(int i = 0;i < buckets;i++){
			list[i] = new LinkedList();
		} //--初始化 list
		
	/*	for(LinkedList l:list)
			l = new LinkedList();
		*///---爲什麼不能用這個進行初始化
		
		int len = a.length;
		
		//尋找數組中最大的元素
		int max = a[0];
		for(int i = 1;i < len;i++){
			if(max < a[i])
				max = a[i];
		}
		
		for(int i = 0;i < len;i++){
			int index = buckets*a[i]/(max+1);
			list[index].add(a[i]);
		}
	/*	for(int i = 0;i < buckets;i++){
			Collections.sort(list[i]);
		}*/
		
		for(LinkedList l:list)
			Collections.sort(l);   //偷個懶直接用api
		
		int p = 0;
		for(int i = 0;i < buckets;i++){
			while(!list[i].isEmpty()){
				a[p] = (int) list[i].remove(0);
				p++;
			}
		}
	}
	
	public static void main(String[] args) {
		int a[] = RandomArray.Array(100,1,9000);
		for(int i:a)
			System.out.print(i+" ");
		bucketSort(a);
		System.out.println("");
		for(int i:a)
			System.out.print(i+" ");
			

	}

}

2、計數排序--桶是數組

package Sort;
/*計數操作--
 * 	思想是--用空間換時間
 * 	開闢一個足夠大的空間數組temp
 * 	如果是對數組a進行排序的話
 * 	a第i個元素的值作爲temp數組index,即 temp[a[i]] == temp[a[i]]++;
 * 	
 * 缺陷
 * 	不能對浮點數進行排序(這裏指的浮點數是指數組中的元素的浮點位數不一樣)
 * 	不能同時對含有重複數的與有負數的數組進行排序
 * 	 	
 */
public class CountSort {
	
	public static void countSort(int a[]){
		//找到最大的元素
		int max = a[0];
		int len = a.length;
		for(int i = 1;i < len;i++){
			if(max < a[i]){
				max = a[i];
			}
		}
		int temp[] = new int[max+1]; //這裏max爲什麼要加1  有沒有必要?---必須要加1.因爲當有最後寫入a[i]是temp的下標,數組的下標是從0開始的沒有max下標
		//對數組temp進行賦值
		for(int i = 0;i < len;i++){
			temp[a[i]] = temp[a[i]] + 1;
		}
			
		//將temp的index順序輸出
		int p = 0;
		for(int i = 0;i < max+1;i++){
			while(temp[i] != 0){
				a[p] = i;
				temp[i] = temp[i] - 1;
				p++;
			}
		}
	}
	public static void main(String[] args) {
		int a[] = RandomArray.Array(100,1,10000);
		for(int i:a)
			System.out.print(i+" ");
		countSort(a);
		System.out.println("");
		for(int i:a)
			System.out.print(i+" ");

	}

}

3、基數排序--桶是線性表

package Sort;
/*
 * 思想是:基數 radix 【數】基數;根值;記數根;【植】根
 * 		對數組元素中的每一個數值的每一個位上進行分佈到各個桶中然後再出桶
 * 		一直循環到最大的那個元素的位數
 */

import java.util.ArrayList;

public class RadixSort {
	
	public static void radixSort(int a[],int radix,int temp[]){
		int len = a.length;
		ArrayList list[] = new ArrayList[10];
		//初始化list數組中的ArrayList
		for(int i = 0;i < 10;i++)
			list[i] = new ArrayList();  ///極重要
		
		//入桶
		for(int i = 0;i < len;i++){
			int number = temp[i] % 10;
			list[number].add(a[i]);
		}
		
		
		int p = 0;  
		//出桶
		for(int i = 0;i < 10;i++){
			int p1 = 0;
			while(!list[i].isEmpty()){
				a[p] = (int) list[i].remove(0);	//因爲是移去的所以每次都是第一個		
				p++;    //p1++
			}
		}
		
		for(int i = 0;i < len;i++){
			temp[i] = (int) (a[i] / (Math.pow(10,radix)));  //
		}
		
	}
	
	public static void radixSort(int a[]){
		int max = a[0];
		int len = a.length;
		for(int i = 1;i < len;i++)
			if(max <= a[i])
				max = a[i];
		int radix = String.valueOf(max).length();
		int temp[] = new int[a.length];
		//temp = a;  //這個語句的含義是啥子:不能將a直接賦值給temp temp = a 是將a的地址給了temp如果改變temp中的元素a中的元素也會改變
		int p = 0;
		for(int i:a){
			temp[p] = i;
			p++;
		}
		
		int count = 1;
		while(radix != (count-1)){
			radixSort(a,count,temp);
			count++;
		}
	}
	
	public static void main(String[] args) {
		int a[] = RandomArray.Array(100,1,9000);
		for(int i:a)
			System.out.print(i+" ");
		radixSort(a);
		System.out.println("");
		for(int i:a)
			System.out.print(i+" ");
		
	}

}

五、堆排序

package Sort;

import java.util.ArrayList;
/*
 * 利用數組構建一個特殊的堆,然後將堆的數據按照特殊的方式重寫到數組上面
 * 1、如何用數組構建堆
 * 		除數組的第一個元素每一個元素都有父節點--數組中的元素的父節點的求法是  (i - 1)/ 2
 * 		除葉子節點外,數組中的每一個元素都有子節點,已知一個父節點再數組中的位於第i個那麼其兩個子節點分別位 2*i+1 2*i+2
 * 2、構建什麼樣特殊的堆
 * 		如果是遞增的:
 * 			父節點比子節點小
 * 			左孩子比右孩子小
 * 3、怎麼讓堆輸出重寫到數組上面
 */
public class HeapSort {
	/*public static void heapSort(int a[]){
		int len = a.length;
		int p = 0;
		int left = 2*p + 1;
		int right = 2*p + 2;
		
		int temp[] = new int[len];
		temp[p] = a[p];
		
		if(a[2*p+1] < temp[p]){
			int t = temp[p];
			temp[p] = a[2*p+1];
			temp[p*2+1] = temp[p];
		}else{
			temp[2*p+1] = a[2*p+1]; 
		}
	}*/
	
	public static ArrayList<Integer> list = new ArrayList<Integer>();
	
	public static void add(int a){
		list.add(a);
		int currentIndex = list.size()-1;
		while(currentIndex > 0){
			
			int parentIndex = (currentIndex - 1) / 2;
			if(list.get(currentIndex) > list.get(parentIndex)){
				int temp = list.get(currentIndex);
				list.set(currentIndex,list.get(parentIndex));
				list.set(parentIndex,temp);
			}else{
				break;
			}
			currentIndex = parentIndex;	
		}
	}
	
	public static int remove(){
		int removeObject = list.get(0);
		
		/**將數組中的最後一個元素(堆的最下面的右邊的一個元素)設置爲 根 之後將其刪除*/
		list.set(0, list.get(list.size() - 1));
		list.remove(list.size() - 1);
		
		
		int currentIndex = 0;
		while(currentIndex < list.size()){
			int leftChildIndex = 2*currentIndex + 1;
			int rightChildIndex = 2*currentIndex + 2;
			//find the maximum between two childern
			if(leftChildIndex >= list.size())	break;   //the tree is heap
			
			int maxIndex = leftChildIndex;
			
			if(rightChildIndex < list.size()){
				if(list.get(maxIndex).compareTo(
						list.get(rightChildIndex))<0){
					maxIndex = rightChildIndex;
				}
			}
			
			//swap if the current node is less than the maximum
			if(list.get(currentIndex).compareTo(
					list.get(maxIndex)) < 0){
				int temp = list.get(maxIndex);
				list.set(maxIndex, list.get(currentIndex));
				list.set(currentIndex, temp);
				currentIndex = maxIndex;
			}else
				break;//the tree is a heap
		}
		return removeObject;
	}
	
	public static void heapSort(int[] list){
		//add elements to the heap
		for(int i = 0;i < list.length;i++)
			add(list[i]);
		
		for(int i = list.length - 1;i >= 0;i--)
			list[i] = remove();
	}
	
	public static void main(String[] args) {
		int a[] = RandomArray.Array(10,1,10);
		for(int i:a){
			System.out.print(i+" ");
		}
		heapSort(a);
		System.out.println("");
		for(int i:a){
			System.out.print(i+" ");
		}
	}

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章