- N +

shellsort排序?shell sort 第n列排序

大家好,今天來為大家解答shellsort排序這個問題的一些問題點,包括shell sort 第n列排序也一樣很多人還不知道,因此呢,今天就來為大家分析分析,現(xiàn)在讓我們一起來看看吧!如果解決了您的問題,還望您關注下本站哦,謝謝~

C語言多項排序

1、穩(wěn)定排序和非穩(wěn)定排序

簡單地說就是所有相等的數(shù)經(jīng)過某種排序方法后,仍能保持它們在排序之前的相對次序,我們就說這種排序方法是穩(wěn)定的。反之,就是非穩(wěn)定的。

比如:一組數(shù)排序前是a1,a2,a3,a4,a5,其中a2=a4,經(jīng)過某種排序后為a1,a2,a4,a3,a5,則我們說這種排序是穩(wěn)定的,因為a2排序前在a4的前面,排序后它還是在a4的前面。假如變成a1,a4,a2,a3,a5就不是穩(wěn)定的了。

2、內(nèi)排序和外排序

在排序過程中,所有需要排序的數(shù)都在內(nèi)存,并在內(nèi)存中調整它們的存儲順序,稱為內(nèi)排序;

在排序過程中,只有部分數(shù)被調入內(nèi)存,并借助內(nèi)存調整數(shù)在外存中的存放順序排序方法稱為外排序。

3、算法的時間復雜度和空間復雜度

所謂算法的時間復雜度,是指執(zhí)行算法所需要的計算工作量。

一個算法的空間復雜度,一般是指執(zhí)行這個算法所需要的內(nèi)存空間。

二、各類排序算法分析

1、冒泡排序

====================================================

算法思想簡單描述:

在要排序的一組數(shù)中,對當前還未排好序的范圍內(nèi)的全部數(shù),自上而下對相鄰的兩個數(shù)依次進行比較和調整,讓較大的數(shù)往下沉,較小的往上冒。即:每當兩相鄰的數(shù)比較后發(fā)現(xiàn)它們的排序與排序要求相反時,就將它們互換。

下面是一種改進的冒泡算法,它記錄了每一遍掃描后最后下沉數(shù)的位置k,這樣可以減少外層循環(huán)掃描的次數(shù)。

冒泡排序是穩(wěn)定的。算法時間復雜度O(n2)--[n的平方]

=====================================================

#include<iostream.h>

voidBubbleSort(int*pData,intCount)

{

intiTemp;

for(inti=1;i<Count;i++)

{

for(intj=Count-1;j>=i;j--)

{

if(pData[j]<pData[j-1])

{

iTemp=pData[j-1];

pData[j-1]=pData[j];

pData[j]=iTemp;

}

}

}

}

voidmain()

{

intdata[]={10,9,8,7,6,5,4};

BubbleSort(data,7);

for(inti=0;i<7;i++)

cout<<data[i]<<"";

cout<<"\n";

}

倒序(最糟情況)

第一輪:10,9,8,7->10,9,7,8->10,7,9,8->7,10,9,8(交換3次)

第二輪:7,10,9,8->7,10,8,9->7,8,10,9(交換2次)

第一輪:7,8,10,9->7,8,9,10(交換1次)

循環(huán)次數(shù):6次

交換次數(shù):6次

其他:

第一輪:8,10,7,9->8,10,7,9->8,7,10,9->7,8,10,9(交換2次)

第二輪:7,8,10,9->7,8,10,9->7,8,10,9(交換0次)

第一輪:7,8,10,9->7,8,9,10(交換1次)

循環(huán)次數(shù):6次

交換次數(shù):3次

上面我們給出了程序段,現(xiàn)在我們分析它:這里,影響我們算法性能的主要部分是循環(huán)和交換,顯然,次數(shù)越多,性能就越差。從上面的程序我們可以看出循環(huán)的次數(shù)是固定的,為1+2+...+n-1。寫成公式就是1/2*(n-1)*n。現(xiàn)在注意,我們給出O方法的定義:

若存在一常量K和起點n0,使當n>=n0時,有f(n)<=K*g(n),則f(n)=O(g(n))。(呵呵,不要說沒學好數(shù)學呀,對于編程數(shù)學是非常重要的!!!)

現(xiàn)在我們來看1/2*(n-1)*n,當K=1/2,n0=1,g(n)=n*n時,1/2*(n-1)*n<=1/2*n*n=K*g(n)。所以f(n)=O(g(n))=O(n*n)。所以我們程序循環(huán)的復雜度為O(n*n)。再看交換。從程序后面所跟的表可以看到,兩種情況的循環(huán)相同,交換不同。其實交換本身同數(shù)據(jù)源的有序程度有極大的關系,當數(shù)據(jù)處于倒序的情況時,交換次數(shù)同循環(huán)一樣(每次循環(huán)判斷都會交換),復雜度為O(n*n)。當數(shù)據(jù)為正序,將不會有交換。復雜度為O(0)。亂序時處于中間狀態(tài)。正是由于這樣的原因,我們通常都是通過循環(huán)次數(shù)來對比算法。

2、選擇排序

====================================================

算法思想簡單描述:

在要排序的一組數(shù)中,選出最小的一個數(shù)與第一個位置的數(shù)交換;然后在剩下的數(shù)當中再找最小的與第二個位置的數(shù)交換,如此循環(huán)到倒數(shù)第二個數(shù)和最后一個數(shù)比較為止。

選擇排序是不穩(wěn)定的。算法復雜度O(n2)--[n的平方]

====================================================

#include<iostream.h>

voidSelectSort(int*pData,intCount)

{

intiTemp;

intiPos;

for(inti=0;i<Count-1;i++)

{

iTemp=pData[i];

iPos=i;

for(intj=i+1;j<Count;j++)

{

if(pData[j]<iTemp)

{

iTemp=pData[j];

iPos=j;

}

}

pData[iPos]=pData[i];

pData[i]=iTemp;

}

}

voidmain()

{

intdata[]={10,9,8,7,6,5,4};

SelectSort(data,7);

for(inti=0;i<7;i++)

cout<<data[i]<<"";

cout<<"\n";

}

倒序(最糟情況)

第一輪:10,9,8,7->(iTemp=9)10,9,8,7->(iTemp=8)10,9,8,7->(iTemp=7)7,9,8,10(交換1次)

第二輪:7,9,8,10->7,9,8,10(iTemp=8)->(iTemp=8)7,8,9,10(交換1次)

第一輪:7,8,9,10->(iTemp=9)7,8,9,10(交換0次)

循環(huán)次數(shù):6次

交換次數(shù):2次

其他:

第一輪:8,10,7,9->(iTemp=8)8,10,7,9->(iTemp=7)8,10,7,9->(iTemp=7)7,10,8,9(交換1次)

第二輪:7,10,8,9->(iTemp=8)7,10,8,9->(iTemp=8)7,8,10,9(交換1次)

第一輪:7,8,10,9->(iTemp=9)7,8,9,10(交換1次)

循環(huán)次數(shù):6次

交換次數(shù):3次

遺憾的是算法需要的循環(huán)次數(shù)依然是1/2*(n-1)*n。所以算法復雜度為O(n*n)。我們來看他的交換。由于每次外層循環(huán)只產(chǎn)生一次交換(只有一個最小值)。所以f(n)<=n所以我們有f(n)=O(n)。所以,在數(shù)據(jù)較亂的時候,可以減少一定的交換次數(shù)。

3、直接插入排序

====================================================

算法思想簡單描述:

在要排序的一組數(shù)中,假設前面(n-1)[n>=2]個數(shù)已經(jīng)是排好順序的,現(xiàn)在要把第n個數(shù)插到前面的有序數(shù)中,使得這n個數(shù)也是排好順序的。如此反復循環(huán),直到全部排好順序。

直接插入排序是穩(wěn)定的。算法時間復雜度O(n2)--[n的平方]

=====================================================

#include<iostream.h>

voidSelectSort(int*pData,intCount)

{

intiTemp;

intiPos;

for(inti=0;i<Count-1;i++)

{

iTemp=pData[i];

iPos=i;

for(intj=i+1;j<Count;j++)

{

if(pData[j]<iTemp)

{

iTemp=pData[j];

iPos=j;

}

}

pData[iPos]=pData[i];

pData[i]=iTemp;

}

}

voidmain()

{

intdata[]={10,9,8,7,6,5,4};

SelectSort(data,7);

for(inti=0;i<7;i++)

cout<<data[i]<<"";

cout<<"\n";

}

倒序(最糟情況)

第一輪:10,9,8,7->9,10,8,7(交換1次)(循環(huán)1次)

第二輪:9,10,8,7->8,9,10,7(交換1次)(循環(huán)2次)

第一輪:8,9,10,7->7,8,9,10(交換1次)(循環(huán)3次)

循環(huán)次數(shù):6次

交換次數(shù):3次

其他:

第一輪:8,10,7,9->8,10,7,9(交換0次)(循環(huán)1次)

第二輪:8,10,7,9->7,8,10,9(交換1次)(循環(huán)2次)

第一輪:7,8,10,9->7,8,9,10(交換1次)(循環(huán)1次)

循環(huán)次數(shù):4次

交換次數(shù):2次

上面結尾的行為分析事實上造成了一種假象,讓我們認為這種算法是簡單算法中最好的,其實不是,因為其循環(huán)次數(shù)雖然并不固定,我們?nèi)钥梢允褂肙方法。從上面的結果可以看出,循環(huán)的次數(shù)f(n)<=1/2*n*(n-1)<=1/2*n*n。所以其復雜度仍為O(n*n)(這里說明一下,其實如果不是為了展示這些簡單排序的不同,交換次數(shù)仍然可以這樣推導)。現(xiàn)在看交換,從外觀上看,交換次數(shù)是O(n)(推導類似選擇法),但我們每次要進行與內(nèi)層循環(huán)相同次數(shù)的‘=’操作。正常的一次交換我們需要三次‘=’而這里顯然多了一些,所以我們浪費了時間。

個人認為在簡單排序算法中,選擇法是最好的。

4、希爾排序

====================================================

算法思想簡單描述:

在直接插入排序算法中,每次插入一個數(shù),使有序序列只增加1個節(jié)點,并且對插入下一個數(shù)沒有提供任何幫助。如果比較相隔較遠距離(稱為增量)的數(shù),使得數(shù)移動時能跨過多個元素,則進行一次比較就可能消除

多個元素交換。D.L.shell于1959年在以他名字命名的排序算法中實現(xiàn)了這一思想。算法先將要排序的一組數(shù)按某個增量d分成若干組,每組中記錄的下標相差d.對每組中全部元素進行排序,然后再用一個較小的增量對它進行,在每組中再進行排序。當增量減到1時,整個要排序的數(shù)被分成

一組,排序完成。

下面的函數(shù)是一個希爾排序算法的一個實現(xiàn),初次取序列的一半為增量,以后每次減半,直到增量為1。

希爾排序是不穩(wěn)定的。

=====================================================

這個排序非常復雜,看了程序就知道了。首先需要一個遞減的步長,這里我們使用的是9、5、3、1(最后的步長必須是1)。工作原理是首先對相隔9-1個元素的所有內(nèi)容排序,然后再使用同樣的方法對相隔5-1個元素的排序,以次類推。

#include<iostream.h>

voidShellSort(int*pData,intCount)

{

intstep[4];

step[0]=9;

step[1]=5;

step[2]=3;

step[3]=1;

inti,Temp;

intk,s,w;

for(inti=0;i<4;i++)

{

k=step[i];

s=-k;

for(intj=k;j<Count;j++)

{

iTemp=pData[j];

w=j-k;//求上step個元素的下標

if(s==0)

{

s=-k;

s++;

pData[s]=iTemp;

}

while((iTemp<pData[w])&&(w>=0)&&(w<=Count))

{

pData[w+k]=pData[w];

w=w-k;

}

pData[w+k]=iTemp;

}

}

}

voidmain()

{

intdata[]={10,9,8,7,6,5,4,3,2,1,-10,-1};

ShellSort(data,12);

for(inti=0;i<12;i++)

cout<<data[i]<<"";

cout<<"\n";

}

呵呵,程序看起來有些頭疼。不過也不是很難,把s==0的塊去掉就輕松多了,這里是避免使用0步長造成程序異常而寫的代碼。這個代碼我認為很值得一看。這個算法的得名是因為其發(fā)明者的名字D.L.SHELL。依照參考資料上的說法:“由于復雜的數(shù)學原因避免使用2的冪次步長,它能降低算法效率。”另外算法的復雜度為n的1.2次冪。同樣因為非常復雜并“我也不知道過程",我們只有結果了。

5、快速排序

====================================================

算法思想簡單描述:

快速排序是對冒泡排序的一種本質改進。它的基本思想是通過一趟掃描后,使得排序序列的長度能大幅度地減少。在冒泡排序中,一次掃描只能確保最大數(shù)值的數(shù)移到正確位置,而待排序序列的長度可能只減少1。快速排序通過一趟掃描,就能確保某個數(shù)(以它為基準點吧)的左邊各數(shù)都比它小,右邊各數(shù)都比它大。然后又用同樣的方法處理它左右兩邊的數(shù),直到基準點的左右只有一個元素為止。它是由C.A.R.Hoare于1962年提出的。

顯然快速排序可以用遞歸實現(xiàn),當然也可以用棧化解遞歸實現(xiàn)。下面的函數(shù)是用遞歸實現(xiàn)的,有興趣的朋友可以改成非遞歸的。

快速排序是不穩(wěn)定的。最理想情況算法時間復雜度O(nlog2n),最壞O(n2)

=====================================================

#include<iostream.h>

voidrun(int*pData,intleft,intright)

{

inti,j;

intmiddle,iTemp;

i=left;

j=right;

middle=pData[(left+right)/2];//求中間值

do{

while((pData[i]<middle)&&(i<right))//從左掃描大于中值的數(shù)

i++;

while((pData[j]>middle)&&(j>left))//從右掃描大于中值的數(shù)

j--;

if(i<=j)//找到了一對值

{

//交換

iTemp=pData[i];

pData[i]=pData[j];

pData[j]=iTemp;

i++;

j--;

}

}while(i<=j);//如果兩邊掃描的下標交錯,就停止(完成一次)

//當左邊部分有值(left<j),遞歸左半邊

if(left<j)

run(pData,left,j);

//當右邊部分有值(right>i),遞歸右半邊

if(right>i)

run(pData,i,right);

}

voidQuickSort(int*pData,intCount)

{

run(pData,0,Count-1);

}

voidmain()

{

intdata[]={10,9,8,7,6,5,4};

QuickSort(data,7);

for(inti=0;i<7;i++)

cout<<data[i]<<"";

cout<<"\n";

}

這里我沒有給出行為的分析,因為這個很簡單,我們直接來分析算法:首先我們考慮最理想的情況

1.數(shù)組的大小是2的冪,這樣分下去始終可以被2整除。假設為2的k次方,即k=log2(n)。

2.每次我們選擇的值剛好是中間值,這樣,數(shù)組才可以被等分。

第一層遞歸,循環(huán)n次,第二層循環(huán)2*(n/2)......所以共有n+2(n/2)+4(n/4)+...+n*(n/n)=n+n+n+...+n=k*n=log2(n)*n所以算法復雜度為O(log2(n)*n)其他的情況只會比這種情況差,最差的情況是每次選擇到的middle都是最小值或最大值,那么他將變成交換法(由于使用了遞歸,情況更糟)。但是你認為這種情況發(fā)生的幾率有多大??呵呵,你完全不必擔心這個問題。實踐證明,大多數(shù)的情況,快速排序總是最好的。如果你擔心這個問題,你可以使用堆排序,這是一種穩(wěn)定的O(log2(n)*n)算法,但是通常情況下速度要慢于快速排序(因為要重組堆)。

6、堆排序

====================================================

算法思想簡單描述:

堆排序是一種樹形選擇排序,是對直接選擇排序的有效改進。

堆的定義如下:具有n個元素的序列(h1,h2,...,hn),當且僅當滿足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1)(i=1,2,...,n/2)時稱之為堆。在這里只討論滿足前者條件的堆。

由堆的定義可以看出,堆頂元素(即第一個元素)必為最大項。完全二叉樹可以很直觀地表示堆的結構。堆頂為根,其它為左子樹、右子樹。

初始時把要排序的數(shù)的序列看作是一棵順序存儲的二叉樹,調整它們的存儲順序,使之成為一個堆,這時堆的根節(jié)點的數(shù)最大。然后將根節(jié)點與堆的最后一個節(jié)點交換。然后對前面(n-1)個數(shù)重新調整使之成為堆。依此類推,直到只有兩個節(jié)點的堆,并對它們作交換,最后得到有n個節(jié)點的有序序列。

從算法描述來看,堆排序需要兩個過程,一是建立堆,二是堆頂與堆的最后一個元素交換位置。所以堆排序有兩個函數(shù)組成。一是建堆的滲透函數(shù),二是反復調用滲透函數(shù)實現(xiàn)排序的函數(shù)。

堆排序是不穩(wěn)定的。算法時間復雜度O(nlog2n)。

====================================================

voidsift(int*x,intn,ints)

{

intt,k,j;

t=*(x+s);

k=s;

j=2*k+1;

while(j

{

if(j

<*(x+j+1))*判斷是否滿足堆的條件:滿足就繼續(xù)下一輪比較,否則調整。*&&*(x+j)/>{

j++;

}

if(t<*(x+j))

{

*(x+k)=*(x+j);

k=j;

j=2*k+1;

}

else

{

break;

}

}

*(x+k)=t;

}

voidheap_sort(int*x,intn)

{

inti,k,t;

int*p;

for(i=n/2-1;i>=0;i--)

{

sift(x,n,i);

}

for(k=n-1;k>=1;k--)

{

t=*(x+0);

*(x+0)=*(x+k);

*(x+k)=t;

sift(x,k,0);

}

}

voidmain()

{

#defineMAX4

int*p,i,a[MAX];

p=a;

printf("Input%dnumberforsorting:\n",MAX);

for(i=0;i

{

scanf("%d",p++);

}

printf("\n");

p=a;

select_sort(p,MAX);

for(p=a,i=0;i

{

printf("%d",*p++);

}

printf("\n");

system("pause");

}

其他的交換法,雙向冒泡法等等就不具體介紹了。

三、幾種排序算法的比較和選擇

1.選取排序方法需要考慮的因素:

(1)待排序的元素數(shù)目n;

(2)元素本身信息量的大小;

(3)關鍵字的結構及其分布情況;

(4)語言工具的條件,輔助空間的大小等。

四、小結:

(1)若n較小(n<=50),則可以采用直接插入排序或直接選擇排序。由于直接插入排序所需的記錄移動操作較直接選擇排序多,因而當記錄本身信息量較大時,用直接選擇排序較好。

(2)若文件的初始狀態(tài)已按關鍵字基本有序,則選用直接插入或冒泡排序為宜。

(3)若n較大,則應采用時間復雜度為O(nlog2n)的排序方法:快速排序、堆排序或歸并排序。快速排序是目前基于比較的內(nèi)部排序法中被認為是最好的方法。

(4)在基于比較排序方法中,每次比較兩個關鍵字的大小之后,僅僅出現(xiàn)兩種可能的轉移,因此可以用一棵二叉樹來描述比較判定過程,由此可以證明:當文件的n個關鍵字隨機分布時,任何借助于"比較"的排序算法,至少需要O(nlog2n)的時間。

(5)當記錄本身信息量較大時,為避免耗費大量時間移動記錄,可以用鏈表作為存儲結構。

JavaScript排序算法之希爾排序的2個實例

代碼示例:

functionshellSort(arr){

letlen=arr.length;

//gap即為增量

for(letgap=Math.floor(len/2);gap>0;gap=Math.floor(gap/2)){

for(leti=gap;i<len;i++){

letj=i;

letcurrent=arr[i];

while(j-gap>=0&&current<arr[j-gap]){

arr[j]=arr[j-gap];

j=j-gap;

}

arr[j]=current;

}

}

}

vararr=[3,5,7,1,4,56,12,78,25,0,9,8,42,37];

shellSort(arr);

OK,本文到此結束,希望對大家有所幫助。

返回列表
上一篇:
下一篇: