【Java資料結構與演算法筆記(一)】常見排序演算法及面試考點總結

alicelmx發表於2019-03-05

一切為了暑期實習,一切為了暑期實習。
我就是試著去面面,實習是不可能實習的,老闆是不會讓我們去的。
哭!!!
以下是正題!!!
2019年3月11日:突然想做成一個整理筆記了,那這個就作為開篇吧~


以下排序排名有先後之分,請注意複習策略~

快速排序

快排特別重要!!!
快排特別重要!!!
快排特別重要!!!

基本思想

  1. 首先在陣列中選擇一個基準點(一般選取第一個數字),
  2. 然後分別從陣列的兩端掃描陣列,設兩個指示標誌(low指向起始位置,high指向末尾),
    ( 首先從後半部分開始,如果發現有元素比該基準點的值小,就交換low和high位置的值,然後從前半部分開始掃秒,發現有元素大於基準點的值,就交換low和high位置的值,如此往復迴圈,直到low>=high,然後把基準點的值放到high這個位置。一次排序就完成了。)
  3. 以後採用遞迴的方式分別對前半部分和後半部分排序,當前半部分和後半部分均有序時該陣列就自然有序了。

程式碼實現

package algorithm;

public class QuickSort {
	public static void main(String[] args) {
		int[] arr={49,38,65,97};
		int low = 0, high = arr.length-1;
		quickSort(arr,low,high);
		
		for(int i=0;i<arr.length;i++)
			System.out.println(arr[i]);
	}
	
	public static void quickSort(int[] arr, int low, int high) {
		if(low >= high) return;
		int index = partition(arr,low,high);
		quickSort(arr,low,index-1);
		quickSort(arr,index+1,high);
	}
	public static int partition(int[] arr, int low, int high) {
		int key = arr[low];
		while(low < high) {
			while(low < high && arr[high] >= key) high --;
			arr[low] = arr[high];
			while(low < high && arr[low] <= key) low ++;
			arr[high] = arr[low];
		}
		arr[high] = key;
		return high;
	}
}

快排的改進思路

詳細的看這裡:https://blog.csdn.net/hacker00011000/article/details/52176100

我看有的面經上有問到這個,趕緊總結下:
(回答的時候別給自己挖坑,選自己能實現的回答)

  1. 優化1:當待排序序列的長度分割到一定大小後,使用插入排序;
  2. 優化2:在一次分割結束後,可以把與Key相等的元素聚在一起,繼續下次分割時,不用再對與key相等元素分割;
  3. 優化3:使用尾遞迴優化遞迴操作;
  4. 優化4:使用並行或多執行緒處理子序列。

氣泡排序

基本思想

相鄰兩節點進行比較,大的向後移一個,經過第一輪兩兩比較和移動,最大的元素移動到了最後,第二輪次大的位於倒數第二個,依次進行。

程式碼實現

package algorithm;

public class BubbleSort {
	public static void main(String[] args) {
		int[] arr={49,38,65,97,76,13,27,49,78,34,12,64,5,4,62,99,98,54,56,17,18,23,34,15,35,25,53,51};
		bubbleSort(arr);
		for(int i=0;i<arr.length;i++)
			System.out.println(arr[i]);
	}
	
	public static void bubbleSort(int[] arr) {
		for(int i=0;i<arr.length-1;i++) {
			for(int j=0;j<arr.length-1-i;j++) {
				if(arr[j]>arr[j+1]) {
					int temp = arr[j];
					arr[j] = arr[j+1];
					arr[j+1] = temp;
				}
			}
		}
	}
}

直接插入排序

基本思想

遍歷陣列,遍歷到i時,a0,a1…ai-1是已經排好序的,取出ai,從ai-1開始向前和每個比較大小,如果小於,則將此位置元素向後移動,繼續先前比較,如果不小於,則放到正在比較的元素之後。可見相等元素比較是,原來靠後的還是拍在後邊,所以插入排序是穩定的。

程式碼實現

package algorithm;

public class InsertSort {
	public static void main(String[] args) {
		int[] arr={49,38,65,97,76,13,27,49,78};
		insertSort(arr);
		for(int i=0;i<arr.length;i++)
			System.out.println(arr[i]);
	}
	public static void insertSort(int[] arr) {
		for(int i=1;i<arr.length;i++) {
			int temp = arr[i];
			int j=i-1;
			while(j>=0 && arr[j]>temp) {
				arr[j+1] = arr[j];
				j--;
			}
			arr[j+1] = temp;
		}
	}
}

歸併排序

基本思想

首先考慮下如何將將二個有序數列合併。這個非常簡單,只要從比較二個數列的第一個數,誰小就先取誰,取了後就在對應數列中刪除這個數。然後再進行比較,如果有數列為空,那直接將另一個數列的資料依次取出即可。

程式碼實現

package algorithm;

public class MergeSort {
	public static void main(String[] args) {
		int[] arr={49,38,65,97,38};
		int low = 0, high = arr.length-1;
		mergeSort(arr,low,high);
		
		for(int i=0;i<arr.length;i++)
			System.out.println(arr[i]);
	}
	
	public static void mergeSort(int[] arr, int low, int high) {
		if(low < high) {
			int mid = (low+high)/2;
			mergeSort(arr,low,mid);
			mergeSort(arr,mid+1,high);
			merge(arr,low,mid,high);
		}
	}
	public static void merge(int[] arr, int low, int mid, int high) {
		int[] temp = new int[high-low+1];
		int k=0,i=low,j=mid+1;
		while(i<=mid && j<=high) {
			if(arr[i]<arr[j]) 
				temp[k++] = arr[i++];
			else
				temp[k++] = arr[j++];
		}
		while(i<=mid)
			temp[k++] = arr[i++];
		while(j<=high)
			temp[k++] = arr[j++];
		
		for(k=0;k<temp.length;k++) {
			arr[low+k] = temp[k];
		}
	}
}

簡單選擇排序

package algorithm;

public class SelectSort {
	public static void main(String[] args) {
		int[] arr={4,1,3,7};
		selectSort(arr);
		for(int i=0;i<arr.length;i++)
			System.out.println(arr[i]);
	}
	public static void selectSort(int[] arr) {
		for(int i=0;i<arr.length;i++) {
			int index=i;
			for(int j=i+1;j<arr.length;j++)
				if(arr[index]>arr[j]) 
					index = j;
			int temp = arr[index];
			arr[index] = arr[i];
			arr[i] = temp;
		}
	}
}

各種排序演算法比較(table)

在這裡插入圖片描述

問題1: 穩定性

  1. 什麼是排序演算法的穩定性(摘自百度百科)

假定在待排序的記錄序列中,存在多個具有相同的關鍵字的記錄,若經過排序,這些記錄的相對次序保持不變,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序後的序列中,r[i]仍在r[j]之前,則稱這種排序演算法是穩定的;否則稱為不穩定的。

  1. 對於不穩定的排序演算法,只要舉出反例,即可說明它的不穩定性;而對於穩定的排序演算法,必須對演算法進行分析從而得到穩定的特性。
  2. 快(快排)些(希爾)選(簡單選擇)一堆(堆排序)這四個都是不穩定的,可以按照
    快些選一堆
    快些選一堆
    快些選一堆
    這個順口溜記,我覺得還是很方便的,考驗之後兩年多了,都沒忘!!

問題2: 空間複雜度

兩種演算法需要特殊記:

  1. 快排:由於需要一個輔助棧來實現遞迴,空間複雜度平均為O(log2n) ,最壞情況下為O(n)
  2. 歸併排序:在合併操作中需要藉助較多的輔助空間用於元素複製,所以為O(n)
  3. 其他演算法都為O(1)

問題3: 時間複雜度

大部分參照上面的table即可,但是需要特別注意快排,我在四場面試問到過兩次

  1. 平均時間複雜度為O(nlog2n),但是在元素基本有序的時候反而效能最低為O(n)
  2. 快排被認定為目前內部排序效能最好的演算法

以上,差不多可以應付面試了,如果有問題及時反饋給我哦~

相關文章