當前位置:編程學習大全網 - 編程語言 - 用c++語言將十個數排序

用c++語言將十個數排序

C++排序算法全集

排序算法是壹種基本並且常用的算法。由於實際工作中處理的數量巨大,所以排序算法對算法本身的速度要求很高。

而壹般我們所謂的算法的性能主要是指算法的復雜度,壹般用O方法來表示。在後面我將給出詳細的說明。

對於排序的算法我想先做壹點簡單的介紹,也是給這篇文章理壹個提綱。

我將按照算法的復雜度,從簡單到難來分析算法。

第壹部分是簡單排序算法,後面妳將看到他們的***同點是算法復雜度為O(N*N)(因為沒有使用word,所以無法打出上標和下標)。

第二部分是高級排序算法,復雜度為O(Log2(N))。這裏我們只介紹壹種算法。另外還有幾種算法因為涉及樹與堆的概念,所以這裏不於討論。

第三部分類似動腦筋。這裏的兩種算法並不是最好的(甚至有最慢的),但是算法本身比較奇特,值得參考(編程的角度)。同時也可以讓我們從另外的角度來認識這個問題。

第四部分是我送給大家的壹個餐後的甜點——壹個基於模板的通用快速排序。由於是模板函數可以對任何數據類型排序(抱歉,裏面使用了壹些論壇專家的呢稱)。

 

現在,讓我們開始吧:

 

壹、簡單排序算法

由於程序比較簡單,所以沒有加什麽註釋。所有的程序都給出了完整的運行代碼,並在我的VC環境下運行通過。因為沒有涉及MFC和WINDOWS的內容,所以在BORLAND C++的平臺上應該也不會有什麽問題的。在代碼的後面給出了運行過程示意,希望對理解有幫助。

1.冒泡法:

這是最原始,也是眾所周知的最慢的算法了。他的名字的由來因為它的工作看來象是冒泡:

#include <iostream.h>

void BubbleSort(int* pData,int Count)

{

int iTemp;

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

{

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

 {

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

{

iTemp = pData[j-1];

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

pData[j] = iTemp;

}

 }

}

}

void main()

{

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

BubbleSort(data,7);

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

 cout<<data<<" ";

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次)

循環次數:6次

交換次數: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次)

循環次數:6次

交換次數:3次

上面我們給出了程序段,現在我們分析它:這裏,影響我們算法性能的主要部分是循環和交換,顯然,次數越多,性能就越差。從上面的程序我們可以看出循環的次數是固定的,為1+2+...+n-1。

寫成公式就是1/2*(n-1)*n。

現在註意,我們給出O方法的定義:

若存在壹常量K和起點n0,使當n>=n0時,有f(n)<=K*g(n),則f(n) = O(g(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)。所以我們程序循環的復雜度為O(n*n)。

再看交換。從程序後面所跟的表可以看到,兩種情況的循環相同,交換不同。其實交換本身同數據源的有序程度有極大的關系,當數據處於倒序的情況時,交換次數同循環壹樣(每次循環判斷都會交換),復雜度為O(n*n)。當數據為正序,將不會有交換。復雜度為O(0)。亂序時處於中間狀態。正是由於這樣的原因,我們通常都是通過循環次數來對比算法。

2.交換法:

交換法的程序最清晰簡單,每次用當前的元素壹壹的同其後的元素比較並交換。

#include <iostream.h>

void ExchangeSort(int* pData,int Count)

{

int iTemp;

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

{

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

 {

if(pData[j]<pData)

{

iTemp = pData;

pData = pData[j];

pData[j] = iTemp;

}

 }

}

}

void main()

{

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

ExchangeSort(data,7);

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

 cout<<data<<" ";

cout<<"\n";

}

倒序(最糟情況)

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

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

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

循環次數:6次

交換次數:6次

其他:

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

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

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

循環次數:6次

交換次數:3次

從運行的表格來看,交換幾乎和冒泡壹樣糟。事實確實如此。循環次數和冒泡壹樣也是1/2*(n-1)*n,所以算法的復雜度仍然是O(n*n)。由於我們無法給出所有的情況,所以只能直接告訴大家他們在交換上面也是壹樣的糟糕(在某些情況下稍好,在某些情況下稍差)。

3.選擇法:

現在我們終於可以看到壹點希望:選擇法,這種方法提高了壹點性能(某些情況下)

這種方法類似我們人為的排序習慣:從數據中選擇最小的同第壹個值交換,在從省下的部分中

選擇最小的與第二個交換,這樣往復下去。

#include <iostream.h>

void SelectSort(int* pData,int Count)

{

int iTemp;

int iPos;

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

{

 iTemp = pData;

 iPos = i;

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

 {

if(pData[j]<iTemp)

{

iTemp = pData[j];

iPos = j;

}

 }

 pData[iPos] = pData;

 pData = iTemp;

}

}

void main()

{

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

SelectSort(data,7);

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

 cout<<data<<" ";

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次)

循環次數:6次

交換次數: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次)

循環次數:6次

交換次數:3次

遺憾的是算法需要的循環次數依然是1/2*(n-1)*n。所以算法復雜度為O(n*n)。

我們來看他的交換。由於每次外層循環只產生壹次交換(只有壹個最小值)。所以f(n)<=n

所以我們有f(n)=O(n)。所以,在數據較亂的時候,可以減少壹定的交換次數。

4.插入法:

插入法較為復雜,它的基本工作原理是抽出牌,在前面的牌中尋找相應的位置插入,然後繼續下壹張

#include <iostream.h>

void InsertSort(int* pData,int Count)

{

int iTemp;

int iPos;

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

{

 iTemp = pData;

 iPos = i-1;

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

 {

pData[iPos+1] = pData[iPos];

iPos--;

 }

 pData[iPos+1] = iTemp;

}

}

void main()

{

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

InsertSort(data,7);

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

 cout<<data<<" ";

cout<<"\n";

}

倒序(最糟情況)

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

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

第壹輪:8,9,10,7->7,8,9,10(交換1次)(循環3次)

循環次數:6次

交換次數:3次

其他:

第壹輪:8,10,7,9->8,10,7,9(交換0次)(循環1次)

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

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

循環次數:4次

交換次數:2次

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

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

二、高級排序算法:

高級排序算法中我們將只介紹這壹種,同時也是目前我所知道(我看過的資料中)的最快的。

它的工作看起來仍然象壹個二叉樹。首先我們選擇壹個中間值middle程序中我們使用數組中間值,然後把比它小的放在左邊,大的放在右邊(具體的實現是從兩邊找,找到壹對後交換)。然後對兩邊分別使用這個過程(最容易的方法——遞歸)。

1.快速排序:

#include <iostream.h>

void run(int* pData,int left,int right)

{

int i,j;

int middle,iTemp;

i = left;

j = right;

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

do{

 while((pData<middle) && (i<right))//從左掃描大於中值的數

i++;  

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

j--;

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

 {

//交換

iTemp = pData;

pData = 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);

}

void QuickSort(int* pData,int Count)

{

run(pData,0,Count-1);

}

void main()

{

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

QuickSort(data,7);

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

 cout<<data<<" ";

cout<<"\n";

}

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

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

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

第壹層遞歸,循環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都是最小值或最大值,那麽他將變成交換法(由於使用了遞歸,情況更糟)。但是妳認為這種情況發生的幾率有多大呵呵,妳完全不必擔心這個問題。實踐證明,大多數的情況,快速排序總是最好的。

如果妳擔心這個問題,妳可以使用堆排序,這是壹種穩定的O(log2(n)*n)算法,但是通常情況下速度要慢於快速排序(因為要重組堆)。

三、其他排序

1.雙向冒泡:

通常的冒泡是單向的,而這裏是雙向的,也就是說還要進行反向的工作。

代碼看起來復雜,仔細理壹下就明白了,是壹個來回震蕩的方式。

寫這段代碼的作者認為這樣可以在冒泡的基礎上減少壹些交換(我不這麽認為,也許我錯了)。

反正我認為這是壹段有趣的代碼,值得壹看。

#include <iostream.h>

void Bubble2Sort(int* pData,int Count)

{

int iTemp;

int left = 1;

int right =Count -1;

int t;

do

{

 //正向的部分

 for(int i=right;i>=left;i--)

 {

if(pData<pData[i-1])

{

iTemp = pData;

pData = pData[i-1];

pData[i-1] = iTemp;

t = i;

}

 }

 left = t+1;

 //反向的部分

 for(i=left;i<right+1;i++)

 {

if(pData<pData[i-1])

{

iTemp = pData;

pData = pData[i-1];

pData[i-1] = iTemp;

t = i;

}

 }

 right = t-1;

}while(left<=right);

}

void main()

{

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

Bubble2Sort(data,7);

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

 cout<<data<<" ";

cout<<"\n";

}

2.SHELL排序

這個排序非常復雜,看了程序就知道了。

首先需要壹個遞減的步長,這裏我們使用的是9、5、3、1(最後的步長必須是1)。

工作原理是首先對相隔9-1個元素的所有內容排序,然後再使用同樣的方法對相隔5-1個元素的排序以次類推。

#include <iostream.h>

void ShellSort(int* pData,int Count)

{

int step[4];

step[0] = 9;

step[1] = 5;

step[2] = 3;

step[3] = 1;

int iTemp;

int k,s,w;

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

{

 k = step;

 s = -k;

 for(int j=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;

 }

}

}

void main()

{

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

ShellSort(data,12);

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

 cout<<data<<" ";

cout<<"\n";

}

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

四、基於模板的通用排序:

這個程序我想就沒有分析的必要了,大家看壹下就可以了。不明白可以在論壇上問。

MyData.h文件

///////////////////////////////////////////////////////

class CMyData

{

public:

CMyData(int Index,char* strData);

CMyData();

virtual ~CMyData();

int m_iIndex;

int GetDataSize(){ return m_iDataSize; };

const char* GetData(){ return m_strDatamember; };

//這裏重載了操作符:

CMyData& operator =(CMyData &SrcData);

bool operator <(CMyData& data );

bool operator >(CMyData& data );

private:

char* m_strDatamember;

int m_iDataSize;

};

////////////////////////////////////////////////////////

MyData.cpp文件

////////////////////////////////////////////////////////

CMyData::CMyData():

m_iIndex(0),

m_iDataSize(0),

m_strDatamember(NULL)

{

}

CMyData::~CMyData()

{

if(m_strDatamember != NULL)

 delete[] m_strDatamember;

m_strDatamember = NULL;

}

CMyData::CMyData(int Index,char* strData):

m_iIndex(Index),

m_iDataSize(0),

m_strDatamember(NULL)

{

m_iDataSize = strlen(strData);

m_strDatamember = new char[m_iDataSize+1];

strcpy(m_strDatamember,strData);

}

CMyData& CMyData::operator =(CMyData &SrcData)

{

m_iIndex = SrcData.m_iIndex;

m_iDataSize = SrcData.GetDataSize();

m_strDatamember = new char[m_iDataSize+1];

strcpy(m_strDatamember,SrcData.GetData());

return *this;

}

bool CMyData::operator <(CMyData& data )

{

return m_iIndex<data.m_iIndex;

}

bool CMyData::operator >(CMyData& data )

{

return m_iIndex>data.m_iIndex;

}

///////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////

//主程序部分

#include <iostream.h>

#include "MyData.h"

template <class T>

void run(T* pData,int left,int right)

{

int i,j;

T middle,iTemp;

i = left;

j = right;

//下面的比較都調用我們重載的操作符函數

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

do{

 while((pData<middle) && (i<right))//從左掃描大於中值的數

i++;  

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

j--;

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

 {

//交換

iTemp = pData;

pData = 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);

}

template <class T>

void QuickSort(T* pData,int Count)

{

run(pData,0,Count-1);

}

void main()

{

CMyData data[] = {

 CMyData(8,"xulion"),

 CMyData(7,"sanzoo"),

 CMyData(6,"wangjun"),

 CMyData(5,"VCKBASE"),

 CMyData(4,"jacky2000"),

 CMyData(3,"cwally"),

 CMyData(2,"VCUSER"),

 CMyData(1,"isdong")

};

QuickSort(data,8);

for (int i=0;i<8;i++)

 cout<<data.m_iIndex<<" "<<data.GetData()<<"\n";

cout<<"\n";

////////////////////////////////////////////////////////

經典C++雙向冒泡排序算法

經典C++雙向冒泡排序算法

hawkman2k 發表於 2003-12-09

#include《iostream.h》

#define max 20 //最多記錄個數

typedef int elemtype;

typedef elemtype recs[max];

void bibubble(recs r,int n)

{

int flag=1; //繼續遍歷時flag置1,已排好序不需遍歷時為0

int i=0, j;

elemtype temp;

while(flag==1)

{

flag=0;

for(j=i+1;j《n-1;j++) //正向遍歷找最大值

if(r[j]》r[j+1])

{

flag=1; //能交換時,說明未排好序,需繼續

temp=r[j];

r[j]=r[j+1];

r[j+1]=temp;

}

for(j=n-i-1;j》=i+1;j--) //反向遍歷

if(r[j]》r[j-1])

{

flag=1; //能交換時,說明未排好序,需繼續

temp=r[j];

r[j]=r[j-1];

r[j-1]=temp;

}

i++;

}

}

void main()

{

recs A={2,5,3,4,6,10,9,8,7,1};

int n=10, i;

cout《《"雙向冒泡排序"《《endl《《"排序前:";

for(i=0;i《n;i++)

cout《《A[i]《《"";

cout《《endl;

cout《《" 排序後: ";

bibubble(A,n);

for(i=0;i《n;i++)

cout《《A[i]《《"";

cout《《endl;

}

  • 上一篇:vb倒計時程序
  • 下一篇:關於HIP—HOP
  • copyright 2024編程學習大全網