面試必備演算法——排序(比較&Java實現)
排序演算法
穩定性和複雜度
不穩定
(1)選擇排序(selection sort)— O(n2)
(2)快速排序(quicksort)— O(nlogn) 平均時間, O(n2) 最壞情況; 對於大的、亂序串列一般認為是最快的已知排序
(3)堆排序 (heapsort)— O(nlogn)
(4)希爾排序 (shell sort)— O(nlogn)
(5)基數排序(radix sort)— O(n·k); 需要 O(n) 額外儲存空間 (K為特徵個數)
穩定
(1)插入排序(insertion sort)— O(n2)
(2) 氣泡排序(bubble sort) — O(n2)
(3) 歸併排序 (merge sort)— O(n log n); 需要 O(n) 額外儲存空間
(4)二叉樹排序(Binary tree sort) — O(nlogn); 需要 O(n) 額外儲存空間
(5)計數排序 (counting sort) — O(n+k); 需要 O(n+k) 額外儲存空間,k為序列中Max-Min+1
(6)桶排序 (bucket sort)— O(n); 需要 O(k) 額外儲存空間
排序演算法中最快的是直接插入排序
各種排序演算法的具體程式設計實現
氣泡排序
package Sort;
import java.util.Arrays;
public class BubbleSort {
public static void main(String[] args) {
int [] a = {1,100,234,44,3,2,4,5};
bubbleSort(a,a.length+1);
System.out.println(Arrays.toString(a));
}
public static int[] bubbleSort(int[] A, int n) {
for (int i=0;i<A.length;i++){
for (int j=i+1;j<A.length;j++){
if (A[i] > A[j]){
int tmp = A[i];
A[i] = A[j];
A[j] = tmp;
}
}
}
return A;
}
}
快速排序
package Sort;
import java.util.Arrays;
public class QuickSort {
public static void main(String[] args) {
int A[] = {1,6,9, 2, 3, 1, 5, 4 };
quickSort(A, 0, 7);
System.out.println(Arrays.toString(A));
}
public static void quickSort(int[] A, int left, int right) {
if (left < right) {
// 一次劃分
int mid = partion(A, left, right);
quickSort(A, 0, mid - 1);
quickSort(A, mid + 1, right);
}
}
public static void swap(int[] A, int l, int r) {
int tmp = A[l];
A[l] = A[r];
A[r] = tmp;
}
public static int partion(int[] a, int left, int right) {
// 軸值,預設選取陣列的第一個數字
while (left < right) {
while (left < right && a[left] <= a[right]) {
right--;
}
if (left<right){
swap(a, left, right);
}
while (left < right && a[left] <= a[right]) {
left++;
}
if (left<right){
swap(a, left, right);
}
}
return left;
}
}
插入排序
package Sort;
public class InsertionSort {
public int[] insertionSort(int[] A, int n) {
int i, j, temp;
for(i = 1; i < n; i++){
temp = A[i];
for(j = i; j > 0 && A[j - 1] > temp; j-- ){
A[j] = A[j - 1];
}
A[j] = temp;
}
return A;
}
}
希爾排序
package Sort;
import java.util.Arrays;
public class ShellSort {
public static void main(String[] args) {
int[] a = { 54, 35, 48, 36, 27, 12, 44, 44, 8, 14, 26, 17, 28 };
sort(a);
System.out.println(Arrays.toString(a));
}
public static void sort(int[] a) {
// 設定步長,預設為陣列長度的一半
int step = a.length / 2;
while (step >= 1) {
for (int i = step; i < a.length; i += step) {
int tmp = a[i];
int j;
for (j = i; j > 0 && a[j - step] > tmp; j -= step) {
a[j] = a[j - step];//元素後移
}
a[j] = tmp;//插入的位置,注意此時j在for迴圈中已經進行了一次--
}
step /= 2;
}
}
}
選擇排序
package Sort;
public class SelectionSort {
public int[] selectionSort(int[] A, int n) {
// write code here
for (int i = 0; i < n - 1; i++) {
int index = i;
int j;
// 找出最小值得元素下標
for (j = i + 1; j < n; j++) {
if (A[j] < A[index]) {
index = j;
}
}
int tmp = A[index];
A[index] = A[i];
A[i] = tmp;
}
return A;
}
}
歸併排序
package Sort;
public class MergeSort {
public static void main(String[] args) {
int[] A = { 1, 4, 3, 2, 5 };
mergeSort(A, 5);
for (int i = 0; i < A.length; i++) {
System.out.println(A[i]);
}
}
public static int[] mergeSort(int[] A, int n) {
// write code here
sort(A, 0, n - 1);
return A;
}
public static void sort(int[] A, int left, int right) {
if (left < right) {
int mid = (left + right) / 2;
sort(A, left, mid);
sort(A, mid + 1, right);
merge(A, left, mid, right);
}
}
public static void merge(int[] A, int left, int mid, int right) {
// 臨時陣列
int n = right - left + 1;
int[] tmpArr = new int[n];
int l = left;
int r = mid + 1;
int t = 0;// 臨時陣列下標元素
// 比較兩子序列元素的大小
while (l <= mid && r <= right) {
if (A[l] < A[r]) {
tmpArr[t++] = A[l++];
} else {
tmpArr[t++] = A[r++];
}
}
// 剩餘的加入臨時陣列
while (l <= mid) {
tmpArr[t++] = A[l++];
}
// 剩餘的加入臨時陣列
while (r <= right) {
tmpArr[t++] = A[r++];
}
// 把臨時陣列元素放回原陣列
for (int i = 0; i < t; i++) {
A[left + i] = tmpArr[i];
}
}
}
堆排序
package Sort;
import java.util.Arrays;
public class HeapSort {
public static void main(String[] args) {
int[] a = { 2, 5, 9, 6, 1, 4, 8, 7, 12, 50 };
sort(a);
System.out.println(Arrays.toString(a));
}
public static void sort(int[] a) {
int len = a.length;
for (int i = 0; i < len - 1; i++) {
// 建堆
buildHeap(a, len - 1 - i);
// 交換堆頂元素和最後一個元素
swap(a, 0, len - 1 - i);
}
}
private static void swap(int[] a, int i, int j) {
// TODO Auto-generated method stub
int tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
public static void buildHeap(int[] a, int lastIndex) {
// 從最後一個節點的父節點開始
for (int i = (lastIndex - 1) / 2; i >= 0; i--) {
// 當前節點存在子節點
while (i * 2 + 1 <= lastIndex) {
// 左節點下標值
int l = i * 2 + 1;
// 右結點下標值
int r = i * 2 + 2;
// 預設左節點為最大值
int biggerIndex = l;
// 存在右結點
if (l < lastIndex) {
// 右結點的值比左節點大
if (a[r] > a[l]) {
biggerIndex = r;
}
}
// 當前節點的值比孩子節點的最小值小,交換
if (a[i] < a[biggerIndex]) {
swap(a, i, biggerIndex);
// 把最大值下標賦給當前節點,進入下一次while迴圈判斷
i = biggerIndex;
} else {
break;
}
}
}
}
}
計數排序
package sort;
public class CountSort {
private static int[] countSort(int[] array,int k)
{
int[] C=new int[k+1];//構造C陣列
int length=array.length,sum=0;//獲取A陣列大小用於構造B陣列
int[] B=new int[length];//構造B陣列
for(int i=0;i<length;i++)
{
C[array[i]]+=1;// 統計A中各元素個數,存入C陣列
}
for(int i=0;i<k+1;i++)//修改C陣列
{
sum+=C[i];
C[i]=sum;
}
for(int i=length-1;i>=0;i--)//遍歷A陣列,構造B陣列
{
B[C[array[i]]-1]=array[i];//將A中該元素放到排序後陣列B中指定的位置
C[array[i]]--;//將C中該元素-1,方便存放下一個同樣大小的元素
}
return B;//將排序好的陣列返回,完成排序
}
public static void main(String[] args)
{
int[] A=new int[]{2,5,3,0,2,3,0,3};
int[] B=countSort(A, 5);
for(int i=0;i<A.length;i++)
{
System.out.println((i+1)+"th:"+B[i]);
}
}
}
基數排序
package sort;
public class RadixSort {
private static void radixSort(int[] array,int d)
{
int n=1;//代表位數對應的數:1,10,100...
int k=0;//儲存每一位排序後的結果用於下一位的排序輸入
int length=array.length;
int[][] bucket=new int[10][length];//排序桶用於儲存每次排序後的結果,這一位上排序結果相同的數字放在同一個桶裡
int[] order=new int[length];//用於儲存每個桶裡有多少個數字
while(n<d)
{
for(int num:array) //將陣列array裡的每個數字放在相應的桶裡
{
int digit=(num/n)%10;
bucket[digit][order[digit]]=num;
order[digit]++;
}
for(int i=0;i<length;i++)//將前一個迴圈生成的桶裡的資料覆蓋到原陣列中用於儲存這一位的排序結果
{
if(order[i]!=0)//這個桶裡有資料,從上到下遍歷這個桶並將資料儲存到原陣列中
{
for(int j=0;j<order[i];j++)
{
array[k]=bucket[i][j];
k++;
}
}
order[i]=0;//將桶裡計數器置0,用於下一次位排序
}
n*=10;
k=0;//將k置0,用於下一輪儲存位排序結果
}
}
public static void main(String[] args)
{
int[] A=new int[]{73,22, 93, 43, 55, 14, 28, 65, 39, 81};
radixSort(A, 100);
for(int num:A)
{
System.out.println(num);
}
}
}
堆排序
//構建大根堆:將array看成完全二叉樹的順序儲存結構
private int[] buildMaxHeap(int[] array){
//從最後一個節點array.length-1的父節點(array.length-1-1)/2開始,直到根節點0,反覆調整堆
for(int i=(array.length-2)/2;i>=0;i--){
adjustDownToUp(array, i,array.length);
}
return array;
}
//將元素array[k]自下往上逐步調整樹形結構
private void adjustDownToUp(int[] array,int k,int length){
int temp = array[k];
for(int i=2*k+1; i<length-1; i=2*i+1){ //i為初始化為節點k的左孩子,沿節點較大的子節點向下調整
if(i<length && array[i]<array[i+1]){ //取節點較大的子節點的下標
i++; //如果節點的右孩子>左孩子,則取右孩子節點的下標
}
if(temp>=array[i]){ //根節點 >=左右子女中關鍵字較大者,調整結束
break;
}else{ //根節點 <左右子女中關鍵字較大者
array[k] = array[i]; //將左右子結點中較大值array[i]調整到雙親節點上
k = i; //【關鍵】修改k值,以便繼續向下調整
}
}
array[k] = temp; //被調整的結點的值放人最終位置
}
相關推薦
面試必備演算法——排序(比較&Java實現)
排序演算法 穩定性和複雜度 不穩定 (1)選擇排序(selection sort)— O(n2) (2)快速排序(quicksort)— O(nlogn) 平均時間, O(n2) 最壞情況; 對
九大排序演算法之插入排序(原理及實現)
1、演算法思路:每趟將一個待排序的元素作為關鍵字,按照其關鍵字值得大小插入到已經排好的部分的適當位置上,知道插入完成。 2、演算法過程 舉個栗子(第一趟的排序過程) 原始序列:49、38、65、97、76、13、27、49 1)開始以第一個元素49為關鍵字,看成一個序列,其餘數看成另
九大排序演算法之選擇排序(原理及實現)
1、演算法思想:選擇排序,從頭至尾掃描序列,找出最小的一個元素,和第一個元素交換,接著從剩下的元素中繼續這種選擇和交換方式,最終得到一個有序序列。 2、演算法過程 舉個栗子(第一趟的排序過程) 原始序列:49、38、65、97、76、13、27、49 1)在進行選擇排
有趣的演算法(七):3分鐘看懂希爾排序(C語言實現)
在上一次的演算法討論中,我們一起學習了直接插入排序。它的原理就是把前i個長度的序列變成有序序列,然後迴圈迭代,直至整個序列都變為有序的。但是說來說去它還是一個時間複雜度為(n^2)的演算法,難道就不能再進一步把時間複雜度降低一階麼?確實,以上幾種演算法相對於之前的O(n^2)
Java實現希爾排序(思路與實現)
希爾排序希爾排序(Shell Sort)是插入排序的一種。也稱縮小增量排序,是直接插入排序演算法的一種更高效的改進版本。希爾排序是非穩定排序演算法。該方法因DL.Shell於1959年提出而得名。 希爾排序是把記錄按下標的一定增量分組,對每組使用直接插入排序演算法排序;隨著增
經典演算法之希爾排序(三種實現)
希爾排序的實質就是分組插入排序,該方法又稱縮小增量排序,因DL.Shell於1959年提出而得名。 該方法的基本思想是:先將整個待排元素序列分割成若干個子序列(由相隔某個“增量”的元素組成的)分別進行直接插入排序,然後依次縮減增量再進行排序,待整個序列中的元素
java實現歸併排序(思想與實現)
歸併排序歸併排序是採用分治法的一個非常典型的應用。歸併排序的思想就是先遞迴分解陣列,再合併陣列。將陣列分解最小之後,然後合併兩個有序陣列,基本思路是比較兩個陣列的最前面的數,誰小就先取誰,取了後相應的指標就往後移一位。然後再比較,直至一個數組為空,最後把另一個數組的剩餘部分複
Java中對List中的物件根據某個屬性排序(Lamda表示式實現)
public class Person { private String id; private String name; private int age; public Person(String id, String name, int age) { this.i
先來先服務演算法(FCFS java實現)
package Arithmetic; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.ByteBuf
資料結構排序演算法之歸併排序(c語言實現)
博主身為大二萌新,第一次學習資料結構,自學到排序的時候,對於書上各種各樣的排序演算法頓覺眼花繚亂,便花了很長的時間盡力把每一個演算法都看懂,但限於水平有限,可能還是理解較淺,於是便將它們逐個地整理實現出來,以便加深理解。 歸併排序就是通過將一個具有n個key記錄的線性表,看
史上最詳細的氣泡排序演算法解析( 程式碼Java版本)
1.問題引入:什麼是氣泡排序?(1)官方解讀:氣泡排序(Bubble Sort),是一種電腦科學領域的較簡單的排序演算法。它重複地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來
排序演算法-合併排序(C語言實現)
都說“演算法是程式的靈魂”,而排序是計算機儲存控制方面不能沒有的操作。它在資料的存取,查詢搜尋,資料統計這些基礎資料操作方面有著重要的應用。所以排序演算法是必須是很有研究的。 這次,我學習的是-歸併排序演算法。據說該演算法是馮諾依曼發明的。這個排序演算法比起直
java實現插入排序(思路和實現)
插入排序(英語:Insertion Sort)是一種簡單直觀的排序演算法。它的工作原理是通過構建有序序列,對於未排序資料,在已排序序列中從後向前掃描,找到相應位置並插入。插入排序在實現上,在從後向前掃描過程中,需要反覆把已排序元素逐步向後挪位,為最新元素提供插入空間。插入排序
資料結構排序演算法之快速排序(c語言實現)
快排的原理就是通過一趟排序將待排記錄分割成獨立的兩部分,其中的一部分記錄的關鍵字均比另一部分記錄的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序。這其中,可以使用遞迴呼叫某一關鍵函式的辦法來實現這樣的功能。 分割的方法就是,選取一個樞軸,將所有關鍵字比它
常見排序演算法總結(基於C++實現)
1.插入排序 1.1 直接插入 基本思想: 將待排序表看作左右兩部分,其中左邊為有序區,右邊為無序區,整個排序過程就是將右邊無序區中的元素逐個插入到左邊的有序區中,以構成新的有序區。 template<typename T> void
【演算法】排序03——看著複雜其實就兩步的堆排序(含程式碼實現)
1、堆排序效能簡介 堆排序也是一種應用分治演算法(建堆時應用了該思想)的排序方法,時間複雜度為無論在最好還是最壞情況下都為O[ n log(n)],相比於穩定的歸併排序或是速度更勝一籌的快速排序(最好情況下O(n)),需要頻繁進行交換以換取O(1)空間複雜度優勢的堆排序,在如今大記憶體的硬體
【演算法】排序04——程式碼簡約而不簡單的希爾排序(含程式碼實現)
1、希爾排序的效能簡介 希爾排序是插入排序的改進型,也因此,它的空間複雜度是O(1)。不過有趣的是,希爾排序的平均時間複雜度計與其增量有關,算起來較為複雜,dalao們的研究認為是O[n^(1.3 ~ 2)]之間。 其實相比於快排、歸併、堆排這些平均之間複雜度為O [ nlog(n) ]的線性
堆排序(C語言實現)
names 博客 鏈接 c語言實現 建立 ron 要求 clas [1] 之前的博客介紹介紹了數組的兩種排序算法:插入排序和歸並排序(採用遞歸),見鏈接http://blog.csdn.net/u013165521/article/detai
歸並排序(C語言實現)
ngs 基本 merge 兩個 它的 efi 分別是 void rec 合並排序(MERGE SORT)是又一類不同的排序方法,合並的含義就是將兩個或兩個以上的有序數據序列合並成一個新的有序數據序列,因此它又叫歸並算法。 它的基本思想就是假
排序(C語言實現)
內部排序 利用 int 分治 arr 個數 size quic 外部排序 讀數據結構與算法分析 插入排序 核心:利用的是從位置0到位置P都是已排序的 所以從位置1開始排序,如果當前位置不對,則和前面元素反復交換重新排序 實現 void InsertionSort