排序2 複雜排序

2021-10-11 02:40:16 字數 4252 閱讀 9777

堆排序是一種選擇排序,遍歷後選擇陣列中最大的值放到堆頂,可用陣列實現,最好和最壞和平均複雜度都為o(nlogn),不穩定。

堆就是一種完全二叉樹,排序特徵是堆頂元素大於子元素(不管子元素之間的排列);完全二叉樹的特徵是層序遍歷等於滿二叉樹的層序遍歷(也就是底層可以沒有右邊的子樹)

用陣列來描述堆的定義有大頂堆和小頂堆,若為大頂堆,則i的父節點為i/2,子節點為2i和2i+1

arr[i]arr[2i] && arr[i]>arr[2i+1]

但是放到陣列中父節點變成了2(i-1) 子節點為2i+1,2i+2

公升序使用大頂堆,就是先將待排序陣列構造成乙個大頂堆,此時堆頂為最大值,將它放到最後乙個(對應陣列中就是最後乙個值),就找到了最大值,然後將剩下的值再次排序可以找到第二大的值,將它放到堆底,如此反覆就可以將所有值排序

堆頂也就是陣列0位置,堆尾就是陣列的最後乙個位置

步驟1.構造大頂堆的時候就是從第乙個非葉節點(也就是倒數第二層,因為從這開始才有子節點i/2-1)開始調整(調整就是將此元素下沉到適合它的位置),然後一直迴圈到0也就是堆頂,這時所有元素都完成下沉。

2.此時已經構造好大頂堆,所以交換堆頂元素到堆底,下沉堆頂元素,迴圈繼續交換,下沉。此時迴圈的i就是未調整的長度可變的,每次都下沉堆頂元素0.

調整的操作是實質就是下沉,需要的是陣列,下沉元素位置,還有最大的陣列長度(也就是除去已經交換完的位置),目的是把arr[i]值下沉到它該去的位置。第一步就是儲存arr[i]為temp,進入for迴圈,從i的左子節點2i+1開始一直到length結束,進入之後首先找到它的兩個子節點中大的那乙個,如果此時大的那個子節點大於i,就把子節點值賦值給父節點(不是交換,而是把arr[i]值和i值都改變成是arr[j],j),然後接著迴圈,一直迴圈到結束或者時子節點值不再大於父節點。for迴圈執行完之後將temp放回到arr[i]處。

邊界條件一定注意

public

static

void

main

(string

argc)

;sort

(arr)

;for

(int i=

0;ipublic

static

void

sort

(int

arr)

for(

int i=arr.length-

1;i>

0;i--)}

public

static

void

sink

(int

arr,

int i,

int length)

if(arr[j]

>temp)

else

} arr[i]

=temp;

}public

static

void

swap

(int

arr,

int lo,

int hi)

歸併的操作就是把兩個以及排序好的佇列歸併merge到一起,兩個就是二路歸併。

時間複雜度為o(nlogn),最好為o(nlogn)最差為o(nlogn),穩定。

所以它的複雜度和堆排序一樣,但穩定,所以工程中物件的排序一般用歸併排序

過程很簡單,就是遞迴,然後歸併操作

歸併merge操作很重要,在歸併排序中需要的是建立乙個新陣列,然後遍歷舊陣列中的兩個部分,將值放到新陣列中,最後全部遍歷完成則將舊陣列的值放到新陣列中。

merge操作的應用有很多,比如說逆序對問題,比如說小和問題。

static

void

mergesorta

(int

a)public

static

void

mergesort

(int

arr,

int lo,

int hi)

int mid=lo+

((hi-lo)

>>1)

;mergesort

(arr,lo,mid)

;mergesort

(arr,mid+

1,hi)

;merge

(arr,lo,mid,hi);}

public

static

void

merge

(int

arr,

int lo,

int mid,

int hi)

else

}while

(p1<=mid)

while

(p2<=hi)

for(

int i=

0;i)}

快速排序利用的主要是partition操作和遞迴,每次都先將陣列分割成兩部分,小於a的放左邊,大於a的放右邊。然後遞迴分割這兩部分。

與歸併排序的區別是歸併是先遞迴,然後歸併排好序的兩部分。快排是先分割,然後遞迴分割兩部分。

平均複雜度為o(nlogn),最好o(n),最壞o(n2),不穩定,所以工程中排序基本資料型別用的就是快速排序。

比起普通的快速排序,改進的地方主要有借鑑了荷蘭國旗問題將partition分成了三部分,第二個改進的地方就是切分的時候是隨機乙個數來切分的。

partiton操作非常重要,主要思想就是通過遍歷把陣列分成前後兩部分或者是三部分,分成三部分需要兩個分界點,分成兩部分需要兩個分界點。

只需要乙個while操作遍歷即可完成分割。

public

static

void

main

(string

argc)

;sort

(arr)

;for

(int i=

0;ipublic

static

void

sort

(int

arr)

quicksort

(arr,

0,arr.length-1)

;}public

static

void

quicksort

(int

arr,

int lo,

int hi)

int[

] mid=

partition

(arr,lo,hi)

;quicksort

(arr,lo,mid[0]

-1);

quicksort

(arr,mid[1]

+1,hi);}

public

static

int[

]partition

(int

arr,

int lo,

int hi)

else

if(arr[cur]

>arr[hi]

)else

}swap

(arr,hi,more++);

return

newint

;}public

static

void

swap

(int

arr,

int lo,

int hi)

partition應用也非常多,就是在考慮分組成幾部分或者要得到某部分的時候非常有用。

1.比如說第乙個是返回超過陣列中一半長度的數字,這個可以根據它的特點知道排好序的中位數肯定是這個數字。所以可以利用partition將隨機乙個數字進行分割,分為前面小於它,後面大於它,中間等於它,如果返回發現中間位置不在等於它的範圍內,就縮小範圍來迴圈partition,直到發現這樣的數在中間了。再看這個數是否超過一半,超過則就是這個數,不超過則返回0。

2.返回陣列中第k大的數字,其實就是乙個partition,先隨機選乙個數字,左邊比它小右邊比它大,然後如果不在第k個接著partition,直到找到第k個數。

3.比如說找出陣列中的前k個數,這個也是要把陣列分成兩半,先以乙個數比如說第k個數partition,分好之後看它的左邊有幾個數,不管小於還是大於都迴圈partition直到分好之後下標為k

關於複雜排序

其實以某個字段排序並不符合客戶要求,客戶要求大多是,某種型別資料排在前面,某種資料排第二,某種第三,但這裡的型別並不是乙個字段可以體現,而是一種邏輯。這裡我們為我們的主表建立兩個排序字段,s1 int s2 int 以 表為例 如果是獨家 則s1列則標記1,s2列則標記設定時間 int格式 如果是推...

複雜排序之歸併排序

歸併排序的時間複雜度是o nlogn 與快速排序和堆排序最大的不同是歸併排序是穩定的,但它需要額外的o n 的空間複雜度 利用遞迴的方法進行排序 void merge elementtype a,elementtype tmpa,int left,int mid,int right void mso...

排序 2 選擇排序

工具方法類 package mydatastructrueadndalgorith.three.arrsort 陣列為模板排序演算法中的一些公用的模板方法 建立人 曹雪坤 version 1.0.0 public class example 遍歷陣列 public static void show ...