排序:
排序大的分類可以分爲兩種:內排序和外排序。在排序過程中,全部記錄存放在內存,則稱爲內排序,如果排序過程中需要使用外存,則稱爲外排序。下面講的排序都是屬於內排序。
內排序有可以分爲以下幾類:
(1)、插入排序:直接插入排序、二分法插入排序、希爾排序。
(2)、選擇排序:簡單選擇排序、堆排序。
(3)、交換排序:冒泡排序、快速排序。
(4)、歸併排序
(5)、基數排序
一:關於各種排序穩定性
冒泡排序、插入排序、歸併排序和基數排序都是穩定的排序算法
參考:http://www.cnblogs.com/Braveliu/archive/2013/01/15/2861201.html
二:關於各種排序的時間複雜度 空間複雜度
一個算法的空間複雜度只考慮在運行過程中爲局部變量分配的存儲空間的大小,它包括爲參數表中形參變量分配的存儲空間和爲在函數體中定義的局部變量分配的存儲空間兩個部分。若一個算法爲遞歸算法,其空間複雜度爲遞歸所使用的堆棧空間的大小,它等於一次調用所分配的臨時存儲空間的大小乘以被調用的次數(即爲遞歸調用的次數加1,這個1表示開始進行的一次非遞歸調用)。算法的空間複雜度一般也以數量級的形式給出。如當一個算法的空間複雜度爲一個常量,即不隨被處理數據量n的大小而改變時,可表示爲O(1);當一個算法的空間複雜度與以2爲底的n的對數成正比時,可表示爲O(log2n);當一個算法的空間複雜度與n成線性比例關係時,可表示爲O(n).若形參爲數組,則只需要爲它分配一個存儲由實參傳送來的一個地址指針的空間,即一個機器字長空間;若形參爲引用方式,則也只需要爲其分配存儲一個地址的空間,用它來存儲對應實參變量的地址,以便由系統自動引用實參變量。
如對於遞歸算法來說,一般都比較簡短,算法本身所佔用的存儲空間較少,但運行時需要一個附加堆棧,從而佔用較多的臨時工作單元;若寫成非遞歸算法,一般可能比較長,算法本身佔用的存儲空間較多,但運行時將可能需要較少的存儲單元。
注意:圖片中快排空間複雜度不正確 最差O(N) 最好O(log2N)
1快速排序 最壞情況:在樞紐元選爲第一個元素,如果輸入時隨機的,那麼是可以接受的,但是若輸入時正序的或者反序,那麼這個樞紐元就會產生一個劣質的分割,因爲所有的元素不是被劃入s1就是被劃入s2 ,更糟糕的是這會發生在所有的遞歸調用,其實什麼也沒幹,因此最壞時間是二次的。我們可以容易的通過樞紐元的選取打到較快的快速排序時間。
2快速排序每次將待排序數組分爲兩個部分,在理想狀況下,每一次都將待排序數組劃分成等長兩個部分,則需要logn次劃分(即遞歸次數)。 而在最壞情況下,即數組已經有序或大致有序的情況下,每次劃分只能減少一個元素,快速排序將不幸退化爲冒泡排序,所以快速排序時間複雜度下界爲O(nlogn),最壞情況爲O(n^2)。在實際應用中,快速排序的平均時間複雜度爲O(nlogn)。快速排序在對序列的操作過程中只需花費常數級的空間。空間複雜度O(1)。 但需要注意遞歸棧上需要花費最少logn 最多n的空間
快速排序空間複雜度只是在通常情況下才爲O(log2n),如果是最壞情況的話,很顯然就要O(n)的空間了。當然,可以通過隨機化選擇pivot來將空間複雜度降低到O(log2n)
3 歸併排序 是遞歸的 需要log2N次 但是至始至終只需要一個N的相同大小數組即可 因此空間複雜度O(N)+O(log2N)=O(N)
三:總結
1、穩定
穩定:冒泡排序、插入排序、歸併排序和基數排序
不穩定:選擇排序、快速排序、希爾排序、堆排序
2、平均時間複雜度
O(n^2):直接插入排序,簡單選擇排序,冒泡排序。
在數據規模較小時(9W內),直接插入排序,簡單選擇排序差不多。當數據較大時,冒泡排序算法的時間代價最高。性能爲O(n^2)的算法基本上是相鄰元素進行比較,基本上都是穩定的。
O(nlogn):快速排序,歸併排序,希爾排序,堆排序
其中,快排是最好的, 其次是歸併和希爾,堆排序在數據量很大時效果明顯。
3、排序算法的選擇
1.數據規模較小
(1)待排序列基本序的情況下,可以選擇直接插入排序;
(2)對穩定性不作要求宜用簡單選擇排序,對穩定性有要求宜用插入或冒泡
2.數據規模不是很大
(1)完全可以用內存空間,序列雜亂無序,對穩定性沒有要求,快速排序,此時要付出log(N)的額外空間。
(2)序列本身可能有序,對穩定性有要求,空間允許下,宜用歸併排序
3.數據規模很大
(1)對穩定性有求,則可考慮歸併排序。
(2)對穩定性沒要求,宜用堆排序
4.序列初始基本有序(正序),宜用直接插入,冒泡
四:代碼實現(Java)
public class 插入_直接插入排序
{
/**
* <插入排序--直接插入排序>
*
* 插入排序:
*
* 思想:每步將一個待排序的記錄,按其順序碼大小插入到前面已經排序的字序列的合適位置,直到全部插入排序完爲止
*
* 關鍵問題:在前面已經排好序的序列中找到合適的插入位置
*
* 直接插入排序:(從後向前找到合適位置後插入)
*/
public static void main(String[] args)
{
//排序之前
int[] arr={10,4,15,6,1,16,12,25,11,2};
for (int a : arr)
{
System.out.print(a+" ");
}
System.out.println();
System.out.println("===============直接插入排序後===================");
for (int i = 1; i < arr.length; i++)
{
//將等待比較的元素拿出來
int temp=arr[i];
int j;
for (j = i-1; j >=0; j--)
{
//大於temp的數都向後移動 注意加了=就不是穩定的了
if(arr[j]>temp)
arr[j+1]=arr[j];
else
break;
}
arr[j+1]=temp;
}
for (int a : arr)
{
System.out.print(a+" ");
}
/*
* 分析
直接插入排序是穩定的排序。關於各種算法的穩定性分析可以參考http://www.cnblogs.com/Braveliu/archive/2013/01/15/2861201.html
文件初態不同時,直接插入排序所耗費的時間有很大差異。若文件初態爲正序,
則每個待插入的記錄只需要比較一次就能夠找到合適的位置插入,故算法的時間複雜度爲O(n),
這時最好的情況。若初態爲反序,則第i個待插入記錄需要比較i+1次才能找到合適位置插入,
故時間複雜度爲O(n2),這時最壞的情況。
直接插入排序的平均時間複雜度爲O(n2)。
*/
}
}
public class 插入_二分插入排序
{
/**
* <插入排序--二分插入排序>
*
* 插入排序:
*
* 思想:每步將一個待排序的記錄,按其順序碼大小插入到前面已經排序的字序列的合適位置,直到全部插入排序完爲止
*
* 關鍵問題:在前面已經排好序的序列中找到合適的插入位置
*
* 二分插入排序:(二分法插入排序的思想和直接插入一樣,只是找合適的插入位置的方式不同,這裏是按二分法找到合適的位置,可以減少比較的次數。)
*/
public static void main(String[] args)
{
//排序之前
int[] arr={10,4,15,6,1,16,12,25,11,2,16};
for (int a : arr)
{
System.out.print(a+" ");
}
System.out.println();
System.out.println("===============二分插入排序後===================");
sort(arr);
for (int a : arr)
{
System.out.print(a+" ");
}
}
private static void sort(int[] arr)
{
for (int i = 0; i < arr.length; i++)
{
//每次都要在前面序列中用二分法找到合適的插入位置
int temp=arr[i];
int left=0;
int right=i-1;
int mid=0;
while(left<=right)
{
mid=(left+right)/2;
if(temp<arr[mid])
right=mid-1;
else
left=mid+1;
}
//此時已經找到插入位置left 整體移位
for (int j = i-1; j >=left; j--)
{
arr[j+1]=arr[j];
}
if(left!=i)//若left等於i 說明序號i之前的序列都比i小 無需交換
arr[left]=temp;
/*
* 當然,二分法插入排序也是穩定的。
二分插入排序的比較次數與待排序記錄的初始狀態無關,
僅依賴於記錄的個數。當n較大時,比直接插入排序的最大比較次數少得多。但大於直接插入排序的最小比較次數。
最好時間複雜度爲O(n)最差時間複雜度爲O(n2) 平均時間複雜度爲O(n2)
*/
}
}
}
public class 插入_希爾排序
{
/**
* <插入排序--希爾排序>
*
* 插入排序:
*
* 思想:每步將一個待排序的記錄,按其順序碼大小插入到前面已經排序的字序列的合適位置,直到全部插入排序完爲止
*
* 關鍵問題:在前面已經排好序的序列中找到合適的插入位置
*
* 希爾排序:基本思想:先取一個小於n的整數d1作爲第一個增量,把文件的全部記錄分成d1個組。
* 所有距離爲d1的倍數的記錄放在同一個組中。先在各組內進行直接插入排序;然後,
* 取第二個增量d2<d1重複上述的分組和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),
* 即所有記錄放在同一組中進行直接插入排序爲止。該方法實質上是一種分組插入方法
*/
public static void main(String[] args)
{
//排序之前
int[] arr={10,4,15,6,1,16,12,25,11,2,16,7};
for (int a : arr)
{
System.out.print(a+" ");
}
System.out.println();
System.out.println("===============希爾排序後===================");
sort(arr);
for (int a : arr)
{
System.out.print(a+" ");
}
}
private static void sort(int[] arr)
{
int d=arr.length/2;
while(true)
{
d/=2;
for (int x = 0; x < d; x++)
{
for (int i = x+d; i < arr.length; i=i+d)
{
int temp=arr[i];
int j;
for (j = i-d; j>=0 && temp<arr[j]; j=j-d)
{
arr[j+d]=arr[j];
}
arr[j+d]=temp;
}
}
if(d==1)
break;
}
/*
* 分析:
* 我們知道一次插入排序是穩定的,但在不同的插入排序過程中,相同的元素可能在各自的插入排序中移動,
* 最後其穩定性就會被打亂,所以希爾排序是不穩定的。
* 希爾排序的時間性能優於直接插入排序,原因如下:
(1)當文件初態基本有序時直接插入排序所需的比較和移動次數均較少。
(2)當n值較小時,n和n2的差別也較小,即直接插入排序的最好時間複雜度O(n)和最壞時間複雜度0(n2)差別不大。
(3)在希爾排序開始時增量較大,分組較多,每組的記錄數目少,故各組內直接插入較快,
後來增量di逐漸縮小,分組數逐漸減少,而各組的記錄數目逐漸增多,但由於已經按di-1作爲距離排過序,
使文件較接近於有序狀態,所以新的一趟排序過程也較快。
因此,希爾排序在效率上較直接插人排序有較大的改進。
希爾排序的平均時間複雜度爲O(nlogn)。沒有最好 最壞
*/
}
}
public class 選擇_簡單選擇排序
{
/**
* <選擇排序--簡單選擇排序>
*
* 插入排序:
*
* 思想:每趟從待排序的記錄序列中選擇關鍵字最小的記錄放置到已排序表的最前位置,直到全部排完。
*
* 關鍵問題:在剩餘的待排序記錄序列中找到最小關鍵碼記錄
*
* 直接插入排序:在要排序的一組數中,選出最小的一個數與第一個位置的數交換;
* 然後在剩下的數當中再找最小的與第二個位置的數交換,如此循環到倒數第二個數和最後一個數比較爲止
*/
public static void main(String[] args)
{
//排序之前
int[] arr={10,4,15,6,1,16,12,25,11,2,9};
for (int a : arr)
{
System.out.print(a+" ");
}
System.out.println();
System.out.println("===============簡答選擇排序後===================");
for (int i = 0; i < arr.length; i++)
{
int min=arr[i];//每次暫時以剩餘的第一個爲最小
int n=i;//記錄最小數的索引位置
for(int j=i+1;j<arr.length;j++)
{
if(arr[j]<min)
{
min=arr[j];
n=j;
}
}
//剩餘的第一個和最小索引處的值進行交換
arr[n]=arr[i];
arr[i]=min;
}
for (int a : arr)
{
System.out.print(a+" ");
}
/*
* 分析
簡單選擇排序是不穩定的排序。
原始序列: 21,25,49,25*,16,08
排序後:08,16, 21,25*,25,49
兩個25的位置變化了,所以是不穩定的
時間複雜度:T(n)=O(n2)
*/
}
}
public class 選擇_堆排序
{
/**
* <選擇排序--堆排序>
*
* 堆排序是一種樹形選擇排序,是對直接選擇排序的有效改進
*
* 堆的定義下:具有n個元素的序列 (h1,h2,...,hn),當且僅當滿足(hi>=h2i,hi>=2i+1)
* 或(hi<=h2i,hi<=2i+1) (i=1,2,...,n/2)時稱之爲堆。在這裏只討論滿足前者條件的堆。由堆的定義可以看出,
* 堆頂元素(即第一個元素)必爲最大項(大頂堆)。完全二 叉樹可以很直觀地表示堆的結構。堆頂爲根,其它爲左子樹、右子樹
*
* 思想:初始時把要排序的數的序列看作是一棵順序存儲的二叉樹,調整它們的存儲序,
* 使之成爲一個 堆,這時堆的根節點的數最大。然後將根節點與堆的最後一個節點交換。
* 然後對前面(n-1)個數重新調整使之成爲堆。依此類推,直到只有兩個節點的堆,並對它們作交換,
* 最後得到有n個節點的有序序列。從算法描述來看,堆排序需要兩個過程,一是建立堆,
* 二是堆頂與堆的最後一個元素交換位置。所以堆排序有兩個函數組成。一是建堆的滲透函數,
* 二是反覆調用滲透函數實現排序的函數
*
*/
public static void main(String[] args)
{
//排序之前 堆序號和數組序號相同
int[] arr={10,4,15,6,1,16,12,25,11,2,9,13};
for (int a : arr)
{
System.out.print(a+" ");
}
System.out.println();
System.out.println("===============堆排序後===================");
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
//左兒子 堆序的下標從0開始
private static int leftChild(int i)
{
return i*2+1;
}
//交換
private static void swap(int[] arr,int i,int j)
{
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
//perculateDown下沉操作 把該數字下方的儘可能小的地方 大的往上走一個
//arrLength數組長度 可變化
private static void perculateDown(int[] arr,int i,int arrLength)
{
int child;
int temp;//把要比的這個值拿出來一個個比 比完了再把此值放到對應的i中
for(temp=arr[i];leftChild(i)<=arrLength-1;i=child)
{
child=leftChild(i);
//如果有右孩子 只有一種情況可能沒有右孩子 就是左孩子爲最後一個孩子時
//若右孩子大 當前i就和右孩子比較
if(child!=arrLength-1 && arr[child]<arr[child+1])
child++;
if(temp<arr[child])
arr[i]=arr[child];
else
break;
}
arr[i]=temp;
}
//先建立堆 利用0-arr.length/2個可能有孩子的節點開始沉降就可以建立堆
//把最大值和最後一個位置交換 再對第一個位置沉降 如此循環
private static void heapSort(int[] arr)
{
//建立堆
for(int i=arr.length/2;i>=0;i--)
{
perculateDown(arr,i,arr.length);
}
//交換並刪除最大
for(int i=arr.length-1;i>0;i--)
{
swap(arr,0,i);
perculateDown(arr,0,i);
}
}
/*
* 堆排序也是一種不穩定的排序算法。
堆排序優於簡單選擇排序的原因:
直接選擇排序中,爲了從R[1..n]中選出關鍵字最小的記錄,必須進行n-1次比較,
然後在R[2..n]中選出關鍵字最小的記錄,又需要做n-2次比較。事實上,後面的n-2次比較中,
有許多比較可能在前面的n-1次比較中已經做過,但由於前一趟排序時未保留這些比較結果,
所以後一趟排序時又重複執行了這些比較操作。
堆排序可通過樹形結構保存部分比較結果,可減少比較次數。
堆排序的最壞時間複雜度爲O(nlogn)。堆序的平均性能較接近於最壞性能。
由於建初始堆所需的比較次數較多,所以堆排序不適宜於記錄數較少的文件。
*/
}
public class 交換_冒泡
{
/**
* <交換排序--冒泡排序>
*
*
* 基本思想:在要排序的一組數中,對當前還未排好序的範圍內的全部數,自上而下對相鄰的兩個數依次進行比較和調整,
* 讓較大的數往下沉,較小的往上冒。即:每當兩相鄰的數比較後發現它們的排序與排序要求相反時,就將它們互換
*/
public static void main(String[] args)
{
//排序之前
int[] arr={10,4,15,6,1,16,12,25,11,2,9,3};
for (int a : arr)
{
System.out.print(a+" ");
}
System.out.println();
System.out.println("===============冒泡排序後===================");
//要把n個數冒出來 沒冒一次 都要比較交換許多次 已經冒出來的 不需要再進行比較
for(int i=0;i<arr.length;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;
}
}
}
System.out.println(Arrays.toString(arr));
/*
* 分析
冒泡排序是一種穩定的排序方法。
•若文件初狀爲正序,則一趟起泡就可完成排序,排序碼的比較次數爲n-1,且沒有記錄移動,時間複雜度是O(n)
•若文件初態爲逆序,則需要n-1趟起泡,每趟進行n-i次排序碼的比較,且每次比較都移動三次,比較和移動次數均達到最大值∶O(n2)
•起泡排序平均時間複雜度爲O(n2)
*/
}
}
public class 交換_快速排序
{
/**
* <交換排序--快速排序>
*
*
* 基本思想:運用遞歸。 先找出樞紐元 分爲兩部分 一部分比樞紐元小 另一部分比它大
* 再進行遞歸
*/
public static void main(String[] args)
{
//排序之前
int[] arr={11,2,9,23,7,5,11,10,8,3,13,4,1};
for (int a : arr)
{
System.out.print(a+" ");
}
System.out.println();
System.out.println("===============快速排序後===================");
quickSort(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
}
//交換
private static void swap(int[] arr,int i,int j)
{
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
//執行三數中值分割法的程序
//把left center right 先排序 center爲樞紐元 再把center和right-1位置進行交換
private static int median3(int[] arr,int left,int right)
{
int center=(left+right)/2;
if(arr[center]<arr[left])
swap(arr,center,left);
if(arr[right]<arr[left])
swap(arr,right,left);
if(arr[right]<arr[center])
swap(arr,right,center);
//把樞紐元位置放在right-1處(程序最後再和i位置交換) j指針從right-2位置開始
swap(arr,center,right-1);
return arr[right-1];
}
private static void quickSort(int[] arr,int left,int right)
{
if(left<right)
{
int pivot=median3(arr,left,right);
int dis=right-left;
if(dis==1 || dis==2)//如果遞歸時 只剩兩個或者三個元素 那麼經過median3就已經排好序了 無需進行下面步驟
return;
int i=left;
int j=right-1;
for(;;)
{
while(arr[++i]<pivot){}
while(arr[--j]>pivot){}
if(i<j)
swap(arr,i,j);
else
break;
}
swap(arr,i,right-1);
quickSort(arr,left,i-1);//排序更大元素
quickSort(arr,i+1,right);//排序更小元素
}
}
/*
* 快速排序是不穩定的排序。
快速排序的時間複雜度爲O(nlogn)。
當n較大時使用快排比較好,當序列基本有序時(若樞紐元取第一個的話)用快排反而不好
相當於每次只分了一個
*/
}
public class 歸併排序
{
/**
* <歸併排序>
*
*
* 基本思想:基本思想:歸併(Merge)排序法是將兩個(或兩個以上)有序表合併成一個新的有序表,
* 即把待排序序列分爲若干個子序列,每個子序列是有序的。然後再把有序子序列合併爲整體有序序列
*/
public static void main(String[] args)
{
//排序之前
int[] arr={11,2,9,23,7,5,11,10,8,3,13,4,1};
for (int a : arr)
{
System.out.print(a+" ");
}
System.out.println();
System.out.println("===============歸併排序後===================");
mergeSort(arr);
System.out.println(Arrays.toString(arr));
}
//自始至終 arr和temp都只有一份
public static void mergeSort(int[] arr)
{
int[] temp=new int[arr.length];
mergeSort(arr,temp,0,arr.length-1);
}
private static void mergeSort(int[] arr, int[] temp, int left, int right)
{
if(left<right)//此處條件需要注意
{
int center=(left+right)/2;
mergeSort(arr,temp,left,center);
mergeSort(arr,temp,center+1,right);
merge(arr,temp,left,center+1,right);
}
}
private static void merge(int[] arr, int[] temp, int leftPos, int rightPos, int rightEnd)
{
int leftEnd=rightPos-1;
int tempPos=leftPos;
int numElements=rightEnd-leftPos+1;
//如果都沒比完
while(leftPos<=leftEnd && rightPos<=rightEnd)
{
if(arr[leftPos]<=arr[rightPos])
temp[tempPos++]=arr[leftPos++];
else
temp[tempPos++]=arr[rightPos++];
}
//如果右邊部分比完了
while(leftPos<=leftEnd)
{
temp[tempPos++]=arr[leftPos++];
}
//如果左邊部分比完了
while(rightPos<=rightEnd)
{
temp[tempPos++]=arr[rightPos++];
}
//把temp copy給arr
for(int i=0;i<numElements;i++,rightEnd--)
{
arr[rightEnd]=temp[rightEnd];
}
}
/*
* 歸併排序是穩定的排序方法。
歸併排序的時間複雜度爲O(nlogn)。
速度僅次於快速排序,爲穩定排序算法,一般用於對總體無序,但是各子項相對有序的數列。
*/
}
public class 基數排序
{
/**
* 基本思想:將所有待比較數值(正整數)統一爲同樣的數位長度,數位較短的數前面補零。
* 然後,從最低位開始,依次進行一次排序。這樣從最低位排序一直到最高位排序完成以後,數列就變成一個有序序列
*/
public static void main(String[] args)
{
//排序之前
int[] arr={11,2,9,23,7,20,5,17,11,10,8,20,3,13,4,1};
for (int a : arr)
{
System.out.print(a+" ");
}
System.out.println();
System.out.println("===============基數排序後===================");
sort(arr);
System.out.println(Arrays.toString(arr));
}
public static void sort(int[] arr)
{
//找到數組中最大值 用來確定位數幾位 來確定循環次數
int max=0;
for(int i=0;i<arr.length;i++)
{
if(arr[i]>max)
max=arr[i];
}
//確定位數
int times=0;
while(max>0)
{
max=max/10;
times++;
}
//建立10個隊列
List<ArrayList> queue=new ArrayList<ArrayList>();
for(int i=0;i<10;i++)
{
ArrayList subQueue=new ArrayList();
queue.add(subQueue);
}
//進行times次分配和收集
for(int i=0;i<times;i++)
{
for(int j=0;j<arr.length;j++)
{
int x=arr[j]%(int)Math.pow(10, i+1)/(int)Math.pow(10, i);
ArrayList subQueue=queue.get(x);
subQueue.add(arr[j]);
// queue.set(x, subQueue);//注意這個可以不寫
}
//收集
int count=0;
for(int k=0;k<10;k++)
{
while(queue.get(k).size()>0)
{
ArrayList<Integer> subQueue=queue.get(k);
arr[count]=subQueue.get(0);
count++;
subQueue.remove(0);//刪除之後會自動移位
}
}
}
}
/*
* 快速排序是不穩定的排序。
基數排序是穩定的排序算法。
基數排序的時間複雜度爲O(d(n+r)),d爲位數,r爲基數。
*/
}
感謝:http://www.cnblogs.com/liuling/p/2013-7-24-01.html
查找:
一、順序查找 條件:無序或有序隊列。 原理:按順序比較每個元素,直到找到關鍵字爲止。 時間複雜度:O(n) 二、二分查找(折半查找) 條件:有序數組 原理:查找過程從數組的中間元素開始,如果中間元素正好是要查找的元素,則搜素過程結束; 如果某一特定元素大於或者小於中間元素,則在數組大於或小於中間元素的那一半中查找,而且跟開始一樣從中間元素開始比較。 如果在某一步驟數組爲空,則代表找不到。 這種搜索算法每一次比較都使搜索範圍縮小一半。 時間複雜度:O(logn) 三、哈希表(散列表) 條件:先創建哈希表(散列表) 原理:根據鍵值方式(Key value)進行查找,通過散列函數,定位數據元素。 時間複雜度:幾乎是O(1),取決於產生衝突的多少。
public class 二分法查找_遞歸
{
/**
* <二分法是對有序數列進行的查找算法>
* <這是遞歸方式的實現>
* @param args
* @see [類、類#方法、類#成員]
*/
public static void main(String[] args)
{
Integer[] a=new Integer[]{2,4,5,7,8,9,11};
int position=search(a,0,a.length-1,1);
if(position!=-1)
System.out.println("二分法查找後爲:"+a[position]);
else
System.out.println("沒找到");
}
//返回的是具體數組位置的序號
public static <T extends Comparable<? super T>> int search(T[] a,int low,int high,T x)
{
if(low>high)
return -1;
int mid=(low+high)/2;
if(a[mid].compareTo(x)>0)
return search(a,low,mid-1,x);
else if(a[mid].compareTo(x)<0)
return search(a,mid+1,high,x);
else
return mid;
}
}
public class 二分法查找_非遞歸
{
/**
* <二分法是對有序數列進行的查找算法>
* <這是非遞歸方式的實現>
* @param args
* @see [類、類#方法、類#成員]
*/
public static void main(String[] args)
{
Integer[] a=new Integer[]{2,4,5,7,8,9,11};
int position=search(a,0,a.length-1,7);
if(position!=-1)
System.out.println("二分法查找後爲:"+a[position]);
else
System.out.println("沒找到");
}
//返回的是具體數組位置的序號
public static <T extends Comparable<? super T>> int search(T[] a,int low,int high,T x)
{
while(low<=high)
{
int mid=(low+high)/2;
if(a[mid].compareTo(x)>0)
high=mid-1;
else if(a[mid].compareTo(x)<0)
low=mid+1;
else
return mid;
}
return -1;
}
}
二叉樹各種遞歸非遞歸遍歷、求深度
public class Node
{
private String element;
public Node()
{
}
public Node(String element)
{
this.element = element;
}
private Node left;
private Node right;
public Node(String element, Node left, Node right)
{
this.element = element;
this.left = left;
this.right = right;
}
public Node getLeft()
{
return left;
}
public void setLeft(Node left)
{
this.left = left;
}
public Node getRight()
{
return right;
}
public void setRight(Node right)
{
this.right = right;
}
/**
* 重載方法
* @return
*/
@Override
public String toString()
{
return element;
}
}
public class BinaryTree
{
private Node root;
public void initTree()
{
Node d = new Node("d");
Node e = new Node("e");
Node f = new Node("f");
Node g = new Node("g");
Node b = new Node("b", d, e);
Node c = new Node("c", f, g);
Node a = new Node("a", b, c);
this.root = a;
}
public Node getRoot()
{
return root;
}
public static void main(String[] args)
{
BinaryTree tree = new BinaryTree();
tree.initTree();
// preorder(tree.getRoot());
// inorder(tree.getRoot());
// postorder(tree.getRoot());
// nonPreorder(tree.getRoot());
// nonInorder(tree.getRoot());
// nonPostorder(tree.getRoot());
//cengxuOrder(tree.getRoot());
//quDongCengxuOrder(tree.getRoot());
//System.out.println(getMaxDepth(tree.getRoot()));
System.out.println(nonGetMaxDepth(tree.getRoot()));
}
/*
* 遞歸實現 前序遍歷
*/
public static void preorder(Node p)
{
if (p != null)
{
System.out.print(p + " ");
preorder(p.getLeft());
preorder(p.getRight());
}
}
/*
* 遞歸實現 前序遍歷
*/
public static void inorder(Node p)
{
if (p != null)
{
inorder(p.getLeft());
System.out.print(p + " ");
inorder(p.getRight());
}
}
/*
* 遞歸實現 後序遍歷
*/
public static void postorder(Node p)
{
if (p != null)
{
postorder(p.getLeft());
postorder(p.getRight());
System.out.print(p + " ");
}
}
// ----------------------非遞歸實現 用Stack------------------------
/*
* 非遞歸實現 前序遍歷
*/
public static void nonPreorder(Node p)
{
Stack<Node> stack = new Stack<Node>();
if (p != null)
{
stack.push(p);// 把根壓入棧
while (!stack.empty())
{
p = stack.pop();
// 前序 先打印當前節點 再把小弟們壓入棧
// 如此往返 小弟先出來打印 第一個小弟出來後 再把它的小弟壓進去 如此往返
System.out.print(p + " ");
if (p.getRight() != null)
stack.push(p.getRight());// 後壓的在上面
if (p.getLeft() != null)
stack.push(p.getLeft());
}
}
}
/*
* 非遞歸實現 中序遍歷 每一個節點 先入站所有最左邊的 然後自己 然後右邊一個子節點 重要 每一個節點先全部 遍歷左堆 再把自己打印 再全部遍歷右堆
*/
public static void nonInorder(Node p)
{
Stack<Node> stack = new Stack<Node>();
while (p != null)// 看右邊第一個節點是否爲空 是的話就結束
{
while (p != null)// 主要看該節點左邊是否爲空
{
if (p.getRight() != null)
stack.push(p.getRight());
stack.push(p);
p = p.getLeft();
}
p = stack.pop();
while (!stack.empty() && p.getRight() == null)// 沒有右字樹 一路打印
{
System.out.print(p + " ");
p = stack.pop();
}
System.out.print(p + " ");
if (!stack.empty())// 若空 表示最頂層節點 沒有右字樹 結束
p = stack.pop();
else
p = null;
}
}
/*
* 非遞歸實現 後序遍歷
*/
public static void nonPostorder(Node p)
{
Node q = p;
Stack<Node> stack = new Stack<Node>();
while (p != null)
{
// 左子樹入棧
for (; p.getLeft() != null; p = p.getLeft())
stack.push(p);
//
while (p != null && (p.getRight() == null || p.getRight() == q))
{
System.out.print(p + " ");
q = p;// 記錄上個已經輸出的節點
if (stack.empty())
return;
p = stack.pop();
}
// 處理右子
stack.push(p);
p = p.getRight();
}
}
/*
* 二叉樹的層序遍歷 非遞歸版本 用隊列Queue(接口) Queue的父接口爲Collection 和 Iterable 子接口包括了LinkedList 層序遍歷二叉樹,用隊列實現,先將根節點入隊列,
* 只要隊列不爲空,然後出隊列,並訪問,接着講訪問節點的左右子樹依次入隊列
*/
public static void cengxuOrder(Node p)
{
if (null == p)
return;
Queue<Node> queue = new LinkedList<Node>();
queue.add(p);
while (!queue.isEmpty())
{
Node temp = queue.poll();
System.out.print(temp + " ");
if (temp.getLeft() != null)
queue.add(temp.getLeft());
if (temp.getRight() != null)
queue.add(temp.getRight());
}
}
public static void quDongCengxuOrder(Node p)
{
List<Node> ls=new ArrayList<Node>();
ls.add(p);
cengxuOrder(p,ls);
}
/*
* 二叉樹層序遍歷 遞歸 對層進行遞歸
*
*/
public static void cengxuOrder(Node p, List<Node> nodes)
{
//空p 或 nodes傳進來的值爲空
if (p == null || nodes.size()==0)
return;
ArrayList<Node> ls = new ArrayList<Node>();
if (nodes.size() > 0)
{
for (Node node : nodes)
{
System.out.print(node + " ");
if (node.getLeft() != null)
ls.add(node.getLeft());
if (node.getRight() != null)
ls.add(node.getRight());
}
}
cengxuOrder(p,ls);
}
/*
* 求二叉樹深度 遞歸算法
*/
public static int getMaxDepth(Node node)
{
if(node==null)
return 0;
int left=getMaxDepth(node.getLeft());
int right=getMaxDepth(node.getRight());
return 1+Math.max(left, right);
}
/*
* 求二叉樹深度 非遞歸算法
*/
public static int nonGetMaxDepth(Node node)
{
if(node==null)
return 0;
int left=1;
int right=1;
int max=1;
//注意 :一定要node=node.getLeft(); 不然一直是死循環
while(node.getLeft()!=null)
{
node=node.getLeft();
left++;
}
while(node.getRight()!=null)
{
node=node.getRight();
right++;
}
return Math.max(left, right);
}
}