1. 快速排序
/*
* 快速排序思想:採用經典的分治思想
* 1、通過一躺排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小
* 2、然後再按次方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列
*/
public class QuikSort {
public static void main(String[] args) {
int arr[]=new int[]{3,5,3,11,0,44,2,9,4,8,1};
quikSort(arr,0,arr.length-1);
print(arr);
}
public static void quikSort(int[] arr,int left,int right){
//遞歸
if(right>left){
int middle=apart(arr,left,right);
quikSort(arr,left,middle-1);
quikSort(arr,middle+1,right);
}
}
//一趟快速排序
public static int apart(int[] arr,int left,int right){
//key值取爲左邊第一個元素
int key=arr[left];
while(left<right){
//從右邊開始,找第一個小於key值的元素
while(arr[right]>=key)
right--;
arr[left]=arr[right];
//從左邊開始,找第一個大於key值的元素
while(right>left&&arr[left]<=key)
left++;
arr[right]=arr[left];
}
//此時left==right
arr[left]=key;
return left;
}
public static void print(int[] arr){
for(int i=0;i<arr.length;i++){
if(i==arr.length-1)
System.out.println(arr[i]);
else
System.out.print(arr[i]+",");
}
}
}
2. 冒泡排序和選擇排序
/*
* 冒泡排序和選擇排序
* 冒泡排序思想:相鄰的兩個元素進行比較,如果符合條件換位
* 選擇排序思想:將數組序列劃分爲無序和有序區,尋找無序區中的最小值和無序區的首元素交換,有序區擴大一個,循環最終完成全部排序
*/
public class BubbleSort {
public static void main(String[] args) {
int arr[]=new int[]{3,5,3,11,0,44,2,9,4,8,1};
System.out.println("原數組:");
print(arr);
System.out.println("冒泡排序後:");
bubbleSort(arr);
print(arr);
System.out.println("選擇排序後:");
bubbleSort(arr);
print(arr);
}
//冒泡排序
public static void bubbleSort(int[] arr)
{
for(int x=0; x<arr.length-1; x++)
{
for(int y=0; y<arr.length-1-x; y++)
{
if(arr[y]>arr[y+1])
{
swap(arr,y,y+1);
}
}
}
}
//選擇排序
public static void selectSort(int[] arr)
{
for(int x=0; x<arr.length-1; x++)
{
for(int y=x+1; y<arr.length; y++)
{
if(arr[x]>arr[y])
{
swap(arr,x,y);
}
}
}
}
//選擇排序,記錄每趟排序的最大值/最小值位置,只將最值與無序區的第1個元素進行交換
public static void selectSort_2(int[] arr)
{
for(int x=0; x<arr.length-1; x++)
{
int num = arr[x];
int index = x;
for(int y=x+1; y<arr.length; y++)
{
if(num>arr[y])
{
num = arr[y];
index = y;
}
}
if(index!=x)
swap(arr,x,index);
}
}
public static void swap(int[] arr,int i,int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public static void print(int[] arr){
for(int i=0;i<arr.length;i++){
if(i==arr.length-1)
System.out.println(arr[i]);
else
System.out.print(arr[i]+",");
}
}
}
3. 插入排序
/*
* 插入排序
* 思想:把n個待排序的元素看成一個有序表和一個無序表。開始時有序表中只包含一個元素,無序表中包含n-1個元素。
* 排序過程中,每次從無序表中取出一個元素,把它的值和有序表中的元素的值進行比較,之後將其插入到有序表中適當的位置,
* 使這個有序表成爲一個新的有序表。
*/
public class InsertSort {
public static void main(String[] args) {
int arr[]=new int[]{3,5,3,11,0,44,2,9,4,8,1};
System.out.println("排序前:");
print(arr);
insertSort(arr);
System.out.println("排序後:");
print(arr);
}
public static void insertSort(int[] arr){
//待排序元素是除arr[0]之外的所有元素
for(int i=1; i<arr.length; i++){
int insertVal = arr[i];
int index = i-1;
//搜索insetVal的插入位置,插入位置之後的元素需要順延移動位置
while(index>=0 && insertVal<arr[index]){
arr[index+1] = arr[index];
index--;
}
arr[index+1] = insertVal;
}
}
public static void print(int[] arr){
for(int i=0;i<arr.length;i++){
if(i==arr.length-1)
System.out.println(arr[i]);
else
System.out.print(arr[i]+",");
}
}
}
4. 堆排序
/*
* 堆排序
* 個關鍵字序列Kl,K2,…,Kn稱爲(Heap),當且僅當該序列滿足如下性質(簡稱爲堆性質):
* ki<=k(2i)且ki<=k(2i+1)(1≤i≤ n),當然,這是小根堆,大根堆則換成>=號。
* k(i)相當於二叉樹的非葉子結點,K(2i)則是左子節點,k(2i+1)是右子節點。
*
* 堆排序利用了大根堆(或小根堆)堆頂記錄的關鍵字最大(或最小)這一特徵,使得在當前無序區中選取最大(或最小)關鍵字的記錄變得簡單。
* 用大根堆排序的基本思想:
* 1.先將初始文件R[1..n]建成一個大根堆,此堆爲初始的無序區
* 2.再將關鍵字最大的記錄R[1](即堆頂)和無序區的最後一個記錄R[n]交換,由此得到新的無序區R[1..n-1]和有序區R[n],且滿足R[1..n-1].keys≤R[n].key
* 3.由於交換後新的根R[1]可能違反堆性質,故應將當前無序區R[1..n-1]調整爲堆。然後再次將R[1..n-1]中關鍵字最大的記錄R[1]和該區間的最後一個記錄R[n-1]交換,由此得到新的無序區R[1..n-2]和有序區R[n-1..n],且仍滿足關係R[1..n-2].keys≤R[n-1..n].keys,同樣要將R[1..n-2]調整爲堆。
* 4.直到無序區只有一個元素爲止。
*
* 特點
* 堆排序(HeapSort)是一樹形選擇排序。堆排序的特點是:在排序過程中,將R[l..n]看成是一棵完全二叉樹的順序存儲結構,
* 利用完全二叉樹中雙親結點和孩子結點之間的內在關係(參見二叉樹的順序存儲結構),在當前無序區中選擇關鍵字最大(或最小)的記錄。
* 區別
* 直接選擇排序中,爲了從R[1..n]中選出關鍵字最小的記錄,必須進行n-1次比較,然後在R[2..n]中選出關鍵字最小的記錄,
* 又需要做n-2次比較。事實上,後面的n-2次比較中,有許多比較可能在前面的n-1次比較中已經做過,
* 但由於前一趟排序時未保留這些比較結果,所以後一趟排序時又重複執行了這些比較操作。
* 堆排序可通過樹形結構保存部分比較結果,可減少比較次數。
*/
public class HeapSort{
private static int[]sort=new int[]{1,0,10,20,3,5,6,4,9,8,12,17,34,11};
public static void main(String[] args){
buildMaxHeapify(sort);
heapSort(sort);
print(sort);
}
private static void buildMaxHeapify(int[]data){
//沒有子節點的才需要創建最大堆,從最後一個的父節點開始
int startIndex=getParentIndex(data.length-1);
//從尾端開始創建最大堆,每次都是正確的堆
for(int i=startIndex;i>=0;i--){
maxHeapify(data,data.length,i);
}
}
/**
*創建最大堆
*heapSize需要創建最大堆的大小,一般在sort的時候用到,因爲最多值放在末尾,末尾就不再歸入最大堆了
*index當前需要創建最大堆的位置
*/
private static void maxHeapify(int[] data,int heapSize,int index){
//當前點與左右子節點比較
int left=getChildLeftIndex(index);
int right=getChildRightIndex(index);
int largest=index;
if(left<heapSize&&data[index]<data[left]){
largest=left;
}
if(right<heapSize&&data[largest]<data[right]){
largest=right;
}
//得到最大值後可能需要交換,如果交換了,其子節點可能就不是最大堆了,需要重新調整
if(largest!=index){
int temp=data[index];
data[index]=data[largest];
data[largest]=temp;
maxHeapify(data,heapSize,largest);
}
}
/**
*排序,最大值放在末尾,data雖然是最大堆,在排序後就成了遞增的
*/
private static void heapSort(int[]data){
//末尾與頭交換,交換後調整最大堆
for(int i=data.length-1;i>0;i--){
int temp=data[0];
data[0]=data[i];
data[i]=temp;
maxHeapify(data,i,0);
}
}
/**
*父節點位置
*/
private static int getParentIndex(int current){
return(current-1)>>1;
}
/**
*左子節點position注意括號,加法優先級更高
*/
private static int getChildLeftIndex(int current){
return(current<<1)+1;
}
/**
*右子節點position
*/
private static int getChildRightIndex(int current){
return(current<<1)+2;
}
private static void print(int[]data){
int pre=-2;
for(int i=0;i<data.length;i++){
if(pre<(int)getLog(i+1)){
pre=(int)getLog(i+1);
System.out.println();
}
System.out.print(data[i]+"|");
}
}
/**
*以2爲底的對數
*/
private static double getLog(double param){
return Math.log(param)/Math.log(2);
}
}
5. 希爾排序/*
* 希爾排序(Shell Sort)是插入排序的一種。是針對直接插入排序算法的改進。
* 該方法又稱縮小增量排序,因DL.Shell於1959年提出而得名。
* 思想:
* 先取一個小於n的整數d1作爲第一個增量,把文件的全部記錄分組。所有距離爲d1的倍數的記錄放在同一個組中。
* 先在各組內進行直接插入排序;然後,取第二個增量d2<d1重複上述的分組和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),
* 即所有記錄放在同一組中進行直接插入排序爲止。
*/
public class ShellSort {
public static void main(String[] args){
int[] a={49,38,65,97,76,13,27,49,78,34,12,64,1};
System.out.println("排序之前:");
for(int i=0;i<a.length;i++){
System.out.print(a[i]+",");
}
//希爾排序
int d=a.length;
while(true){
d=d/2;
for(int x=0;x<d;x++){
for(int i=x+d;i<a.length;i=i+d){
int temp=a[i];
int j;
for(j=i-d;j>=0&&a[j]>temp;j=j-d){
a[j+d]=a[j];
}
a[j+d]=temp;
}
}
if(d==1){
break;
}
}
System.out.println();
System.out.println("排序之後:");
for(int i=0;i<a.length;i++){
System.out.print(a[i]+",");
}
}
}
6. 合併排序/*
* 歸併(Merge)排序法是將兩個(或兩個以上)有序表合併成一個新的有序表,即把待排序序列分爲若干個有序的子序列,
* 再把有序的子序列合併爲整體有序序列。--分治思想
* 歸併操作的工作原理如下:
* 第一步:申請空間,使其大小爲兩個已經排序序列之和,該空間用來存放合併後的序列
* 第二步:設定兩個指針,最初位置分別爲兩個已經排序序列的起始位置
* 第三步:比較兩個指針所指向的元素,選擇相對小的元素放入到合併空間,並移動指針到下一位置
* 重複步驟3直到某一指針超出序列尾
* 將另一序列剩下的所有元素直接複製到合併序列尾
*/
public class MergeSort{
/*
* 二路歸併
* 原理:將兩個有序表合併和一個有序表
* s:第一個有序表的起始下標
* m:第二個有序表的起始下標
* t:第二個有序表的結束小標
*/
private static void merge(int[] a,int s,int m,int t){
int[] tmp=new int[t-s+1];
int i=s,j=m,k=0;
while(i<m&&j<=t){
if(a[i]<=a[j]){
tmp[k]=a[i];
k++;
i++;
}
else{
tmp[k]=a[j];
j++;
k++;
}
}
while(i<m){
tmp[k]=a[i];
i++;
k++;
}
while(j<=t){
tmp[k]=a[j];
j++;
k++;
}
System.arraycopy(tmp,0,a,s,tmp.length);
}
/*
* 每次歸併的有序集合的長度
*/
public static void mergeSort(int[] a,int s, int len){
int size=a.length;
int mid=size/(len<<1);
int c=size&((len<<1)-1);
// -------歸併到只剩一個有序集合的時候結束算法-------//
if(mid==0)
return;
// ------進行一趟歸併排序-------//
for(int i=0;i<mid;++i){
s=i*2*len;
merge(a,s,s+len,(len<<1)+s-1);
}
// -------將剩下的數和倒數一個有序集合歸併-------//
if(c!=0)
merge(a,size-c-2*len,size-c,size-1);
// -------遞歸執行下一趟歸併排序------//
mergeSort(a,0,2*len);
}
public static void main(String[] args){
int[] a=new int[]{4,3,6,1,2,5};
mergeSort(a,0,1);
for(int i=0;i<a.length;++i){
System.out.print(a[i]+" ");
}
}
}