二次排序演算法
① 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[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次)
循環次數: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[i])
{
iTemp = pData[i];
pData[i] = 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[i]<<" ";
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[i];
iPos = i;
for(int j=i+1;j<Count;j++)
{
if(pData[j]<iTemp)
{
iTemp = pData[j];
iPos = j;
}
}
pData[iPos] = pData[i];
pData[i] = iTemp;
}
}
void main()
{
int data[] = {10,9,8,7,6,5,4};
SelectSort(data,7);
for (int i=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次)
循環次數: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[i];
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[i]<<" ";
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[i]<middle) && (i<right))//從左掃描大於中值的數
i++;
while((pData[j]>middle) && (j>left))//從右掃描大於中值的數
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);
}
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[i]<<" ";
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[i]<pData[i-1])
{
iTemp = pData[i];
pData[i] = pData[i-1];
pData[i-1] = iTemp;
t = i;
}
}
left = t+1;
//反向的部分
for(i=left;i<right+1;i++)
{
if(pData[i]<pData[i-1])
{
iTemp = pData[i];
pData[i] = 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[i]<<" ";
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[i];
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[i]<<" ";
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[i]<middle) && (i<right))//從左掃描大於中值的數
i++;
while((pData[j]>middle) && (j>left))//從右掃描大於中值的數
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);
}
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[i].m_iIndex<<" "<<data[i].GetData()<<"\n";
cout<<"\n";
}
② 排序演算法有多少種
排序(Sorting) 是計算機程序設計中的一種重要操作,它的功能是將一個數據元素(或記錄)的任意序列,重新排列成一個關鍵字有序的序列。
排序就是把集合中的元素按照一定的次序排序在一起。一般來說有升序排列和降序排列2種排序,在演算法中有8中基本排序:
(1)冒泡排序;
(2)選擇排序;
(3)插入排序;
(4)希爾排序;
(5)歸並排序;
(6)快速排序;
(7)基數排序;
(8)堆排序;
(9)計數排序;
(10)桶排序。
插入排序
插入排序演算法是基於某序列已經有序排列的情況下,通過一次插入一個元素的方式按照原有排序方式增加元素。這種比較是從該有序序列的最末端開始執行,即要插入序列中的元素最先和有序序列中最大的元素比較,若其大於該最大元素,則可直接插入最大元素的後面即可,否則再向前一位比較查找直至找到應該插入的位置為止。插入排序的基本思想是,每次將1個待排序的記錄按其關鍵字大小插入到前面已經排好序的子序列中,尋找最適當的位置,直至全部記錄插入完畢。執行過程中,若遇到和插入元素相等的位置,則將要插人的元素放在該相等元素的後面,因此插入該元素後並未改變原序列的前後順序。我們認為插入排序也是一種穩定的排序方法。插入排序分直接插入排序、折半插入排序和希爾排序3類。
冒泡排序
冒泡排序演算法是把較小的元素往前調或者把較大的元素往後調。這種方法主要是通過對相鄰兩個元素進行大小的比較,根據比較結果和演算法規則對該二元素的位置進行交換,這樣逐個依次進行比較和交換,就能達到排序目的。冒泡排序的基本思想是,首先將第1個和第2個記錄的關鍵字比較大小,如果是逆序的,就將這兩個記錄進行交換,再對第2個和第3個記錄的關鍵字進行比較,依次類推,重復進行上述計算,直至完成第(n一1)個和第n個記錄的關鍵字之間的比較,此後,再按照上述過程進行第2次、第3次排序,直至整個序列有序為止。排序過程中要特別注意的是,當相鄰兩個元素大小一致時,這一步操作就不需要交換位置,因此也說明冒泡排序是一種嚴格的穩定排序演算法,它不改變序列中相同元素之間的相對位置關系。
選擇排序
選擇排序演算法的基本思路是為每一個位置選擇當前最小的元素。選擇排序的基本思想是,基於直接選擇排序和堆排序這兩種基本的簡單排序方法。首先從第1個位置開始對全部元素進行選擇,選出全部元素中最小的給該位置,再對第2個位置進行選擇,在剩餘元素中選擇最小的給該位置即可;以此類推,重復進行「最小元素」的選擇,直至完成第(n-1)個位置的元素選擇,則第n個位置就只剩唯一的最大元素,此時不需再進行選擇。使用這種排序時,要注意其中一個不同於冒泡法的細節。舉例說明:序列58539.我們知道第一遍選擇第1個元素「5」會和元素「3」交換,那麼原序列中的兩個相同元素「5」之間的前後相對順序就發生了改變。因此,我們說選擇排序不是穩定的排序演算法,它在計算過程中會破壞穩定性。
快速排序
快速排序的基本思想是:通過一趟排序演算法把所需要排序的序列的元素分割成兩大塊,其中,一部分的元素都要小於或等於另外一部分的序列元素,然後仍根據該種方法對劃分後的這兩塊序列的元素分別再次實行快速排序演算法,排序實現的整個過程可以是遞歸的來進行調用,最終能夠實現將所需排序的無序序列元素變為一個有序的序列。
歸並排序
歸並排序演算法就是把序列遞歸劃分成為一個個短序列,以其中只有1個元素的直接序列或者只有2個元素的序列作為短序列的遞歸出口,再將全部有序的短序列按照一定的規則進行排序為長序列。歸並排序融合了分治策略,即將含有n個記錄的初始序列中的每個記錄均視為長度為1的子序列,再將這n個子序列兩兩合並得到n/2個長度為2(當凡為奇數時會出現長度為l的情況)的有序子序列;將上述步驟重復操作,直至得到1個長度為n的有序長序列。需要注意的是,在進行元素比較和交換時,若兩個元素大小相等則不必刻意交換位置,因此該演算法不會破壞序列的穩定性,即歸並排序也是穩定的排序演算法。
③ VB 關於選擇排序演算法
'在窗體上增加控制項command1,label1,然後復制以下代碼
Option Explicit
Dim a(9) As Long
Private Sub Command1_Click()
Dim i As Long, l As Long, n As Long
Dim j As Long, s As String
For i = 0 To 9 '使用選擇排序法排序,每次選擇最小的數值。
For l = i To 9
If a(i) > a(l) Then
n = a(i)
a(i) = a(l)
a(l) = n
End If
Next l
Print a(i)
s = ""
For j = 0 To 9
s = s & a(j) & vbNewLine
Next
MsgBox s, vbInformation, "階段排序情況"
Next i
End Sub
Private Sub Form_Load()
a(0) = 564 '給數組a中個數組元素賦值。
a(1) = 78
a(2) = 45
a(3) = 456412
a(4) = 456
a(5) = 1
a(6) = 45 + 79
a(7) = 12
a(8) = 1 * 966
a(9) = 65 / 5
Dim i As Long
For i = 0 To 9
Label1.Caption = Label1.Caption & "第" & CStr(i + 1) & "是:" & CStr(a(i)) & " "
Next i
End Sub
④ 數據結構的排序方法有哪些
冒泡排序,快速排序,堆排序。
⑤ Java 直接插入 排序演算法 要怎麼應用
直接插入排序流程如下:
1、首先比較數組的前兩個數據,並排序;
2、比較第三個元素與前兩個排好序的數據,並將第三個元素放入適當的位置;
3、比較第四個元素與前三個排好序的數據,並將第四個元素放入適當的位置;
......
4、直至把最後一個元素放入適當的位置。
舉例說明:要排序數組:int[] arr = {7, 2, 6, 5, 9, 4};
第1趟後:[2, 7], 6, 5, 9, 4
第2趟後:[2, 6, 7], 5, 9, 4
第3趟後:[2, 5, 6, 7], 9, 4
第4趟後:[2, 5, 6, 7, 9], 4
第5趟後:[2, 4, 5, 6, 7, 9]
演算法分析
空間復雜度O(1)
時間復雜度O(n2)
最差情況:反序,需要移動n*(n-1)/2個元素
最好情況:正序,不需要移動元素
數組在已排序或者是「近似排序」時,插入排序效率的最好情況運行時間為O(n);
插入排序最壞情況運行時間和平均情況運行時間都為O(n2)。
通常,插入排序呈現出二次排序演算法中的最佳性能。
對於具有較少元素(如n<=15)的列表來說,二次演算法十分有效。
⑥ 用C++實現二叉排序樹的各種演算法
#include<stdio.h>
#include<stdlib.h>
#define D 2
#define R 10
#define N 11
typedef int KeyType;
typedef int DataType;
struct Node;
typedef struct Node radixNode;
struct Node
{
KeyType key[D];
/* DataType info; */
radixNode *next;
};
typedef radixNode * radixList;
typedef struct QueueNode
{
radixNode *f;
radixNode *e;
} Queue;
Queue queue[R];
void display(radixNode *plist)
{
int i;
radixNode *p;
p=plist->next;
while(p!=NULL)
{
printf("->");
for(i=0;i<D;i++)
printf("%1d",p->key[i]);
p=p->next;
}
printf("\n");
}
void radixSort(radixNode *plist,int d,int r)
{
int i,j,k;
radixNode *p,*head;
head=plist->next;
for(j=d-1;j>=0;j--)
{
p=head;
for(i=0;i<r;i++)
{
queue[i].f=NULL;
queue[i].e=NULL;
}
while(p!=NULL)
{
k=p->key[j];
if(queue[k].f==NULL)
queue[k].f=p;
else
(queue[k].e)->next=p;
queue[k].e=p;
p=p->next;
}
i=0;
while(queue[i].f==NULL)
i++;
p=queue[i].e; head=queue[i].f;
for(i++;i<r; i++)
if(queue[i].f!=NULL)
{
p->next=queue[i].f;
p=queue[i].e;
}
p->next=NULL;
printf("j=%d",j);
}
plist->next=head;
}
void main()
{
radixNode *p,*q,*head;
int a[]={30,12,20,17,40,60,34,42,29,35,47};
int i;
p=(radixNode *) malloc(sizeof(struct Node));
head=p;
p->next=NULL;
printf("test...\n");
for(i=0;i<11;i++)
{
q=(radixNode *) malloc(sizeof(struct Node));
q->key[0]=a[i]/10;
q->key[1]=a[i]%10;
q->next=NULL;
p->next=q;
p=p->next;
}
printf("before:\n");
display(head);
printf("\n");
radixSort(head,D,R);
printf("after:\n");
display(head);
}
⑦ 用歸並排序演算法對序列1234567需要幾次
排序三次
歸並排序的方法就是分組排序
假設序列是5,3,7,2,1,4,6
第一次排序每組為2個元素,即分為4組(7/2取上整等於4),分別為【5,3】、【7,2】、【1,4】、【6】,對每一組進行排序;
第一次排序後序列是3,5,2,7,1,4,6
第二次排序每組為4個元素,即分為2組(7/2/2取上整,實際上是第一次分的4組兩兩合並,即4/2取上整等於2),分別為【3,5,2,7】、【1,4,6】
第二次排序後序列是2,3,5,7,1,4,6
第三次排序每組為8個元素,即只有1組,排序完後就是1,2,3,4,5,6,7了
歸並排序第N次排序每組為2^N個,假設有S個元素,一共需要排序Y次,那麼最後一次排序每組就是2^Y個,令2^Y>=S,答案很容易就知道了
⑧ 二叉排序演算法的實現
#include<iostream>
#include<deque>
#include<algorithm>
using namespace std;
struct BinaryNode
{
char ch;//數據域
BinaryNode *leftChild;//左子節點
BinaryNode *rightChild;//右子節點
BinaryNode(BinaryNode *left=NULL,BinaryNode *right=NULL)
{
rightChild=right;
leftChild=left;
}
BinaryNode(char chz,BinaryNode *left=NULL,BinaryNode *right=NULL)
{
ch=chz;
leftChild=left;
rightChild=right;
}
};
class BinaryTree
{
private:
BinaryNode *root; //根節點
char ref;
void preOrder(BinaryNode *p); //前序遍歷
void levelOrder(BinaryNode *p); //層序遍歷
void CreateTree(BinaryNode *&p); //根據前序遍歷建立二叉樹
void deleteTree(BinaryNode *p); //釋放二叉樹
BinaryNode* CreateBinary(char *VLR,char *LVR,int preStart,int inStart,int n); //根據二叉樹的前序遍歷和中序遍歷建立二叉樹
public:
BinaryTree(){root=NULL;ref='#';} //建立空二叉樹
~BinaryTree(); //析構函數
void preOrder(); //前序遍歷
void levelOrder(); //層序遍歷
void CreateTree(); //根據前序遍歷建立二叉樹
void CreateBinary(char *VLR,char *LVR,int n); //用二叉樹的前序遍歷與後序遍歷建立二叉樹
};
void BinaryTree::deleteTree(BinaryNode *p)
//私有函數,釋放二叉樹
{
if(p!=NULL)
{
deleteTree(p->leftChild);
deleteTree(p->rightChild);
delete p;
}
}
BinaryTree::~BinaryTree() //析構函數
{
deleteTree(root);
}
void BinaryTree::preOrder(BinaryNode *p) //私有函數,前序遍歷
{
if(p!=NULL)
{
cout<<p->ch<<" ";
preOrder(p->leftChild);
preOrder(p->rightChild);
}
}
void BinaryTree::preOrder() //前序遍歷
{
preOrder(root);
}
void BinaryTree::levelOrder(BinaryNode *p) //私有函數,層序遍歷
{
deque<BinaryNode*> level; //利用隊列實現
BinaryNode *q=NULL;
if(p!=NULL)
{
level.push_back(p);
}
while(!level.empty())
{
q=level.front();
cout<<q->ch<<" ";
level.pop_front();
if(q->leftChild!=NULL)
level.push_back(q->leftChild); //左子樹入隊列
if(q->rightChild!=NULL)
level.push_back(q->rightChild); //右子樹入隊列
}
}
void BinaryTree::levelOrder() //層序遍歷
{
levelOrder(root);
}
void BinaryTree::CreateTree(BinaryNode *&p) //引用型參數的的用法*&p
{
char item;
cout<<"請輸入節點值: ";
cin>>item;
if(item!=ref) //結束標記ref
{
p=new BinaryNode(item);
CreateTree(p->leftChild); //遞歸建立左子樹
CreateTree(p->rightChild); //遞歸建立右子樹
}
else
p=NULL;
}
void BinaryTree::CreateTree()
{
CreateTree(root);
}
BinaryNode* BinaryTree::CreateBinary(char* VLR,char* LVR,int preStart,int inStart,int n) //私有函數,利用二叉樹的前序遍歷與中序遍歷建立二叉樹
{
BinaryNode *p=NULL;
if(n>0)
{
char elem=VLR[preStart];
p=new BinaryNode(elem);
int i=0;
while(i<n&&elem!=LVR[inStart+i])
i++;
p->leftChild=CreateBinary(VLR,LVR,preStart+1,inStart,i);
p->rightChild=CreateBinary(VLR,LVR,preStart+i+1,inStart+i+1,n-i-1);
}
return p;
}
void BinaryTree::CreateBinary(char *VLR,char *LVR,int n) //利用二叉樹的前序遍歷與中序遍歷建立二叉樹
{
root=CreateBinary(VLR,LVR,0,0,n);
}
int main()
{
BinaryTree tree;
tree.CreateTree();
cout<<"該二叉樹的前序遍歷為: ";
tree.preOrder();
cout<<endl;
cout<<"該二叉樹的層遍序歷為: ";
tree.levelOrder();
cout<<endl;
return 0;
}
⑨ 為什麼我使用直接選擇排序演算法 會頂替掉原來的值
34,56,98,78,23,11,9,54,22,71
第一次排序: 9,56,98,78,34,23,11,54,22,71
第二次排序: 9,11,98,78,56,34,23,54,22,71
第三次排序: 9,11,22,98,78,56,34,54,23,71
第四次排序: 9,11,22,23,98,78,56,54,34,71
第五次排序: 9,11,22,23,34,98,78,56,54,71
第六次排序: 9,11,22,23,34,54,98,78,56,71
第七次排序: 9,11,22,23,34,54,56,98,78,71
第八次排序: 9,11,22,23,34,54,56,,71,98,78
第九次排序: 9,11,22,23,34,54,56,,71,78,98
從第2個元素開始,依次與第一個元素作比較,如果比第一個元素小,則交換,否則找下一個元素與第一個元素比較;
從第3個元素開始,重復第2步,直到比較完
⑩ 誰能給我幾種排序的具體演算法(直接插入,折半插入,冒泡,簡單選擇,快速,堆,歸並排序)
直接插入排序
說明:逐個將後一個數加到前面的排好的序中。在直接插入排序過程中,對其中一個記錄的插入排序稱為一次
排序;直接插入排序是從第二個記錄開始進行的,因此,長度為n的記錄序列需要進行n-1次排序才能完成整個
序列的排序。時間復雜度為O(n2)。
void InsertSort(elemtype x[],int n)
/*用直接插入法對x[0]-x[n-1]排序*/
{
int i,j;
elemtype s;
for(i=0;i<n-1;i++)
{
s=x[i+1];
j=i;
while(j>-1&&s.key<x[j].key)
{
x[j+1]=x[j];
j--;
}
x[j+1]=s;
}
}
---------------------
希爾排序
說明:希爾排序又稱縮小增量排序,增量di可以有各種不同的取法,但最後一次排序時的增量必須為1,最簡
單可取di+1=di/2(取小)。時間復雜度為O(n(log2n)2)。
void ShellSort(elemtype x[],int n,intd[],int Number)
/*用希爾排序法對記錄x[0]-x[n-1]排序,d為增量值數組*/
/*Number為增量值個數,各組內採用直接插入法排序*/
{
int i,j,k,m,Span;
elemtype s;
for(m=0;m<Number;m++)
{
Span=d[m];
for(k=0;k<Span;k++)
{
for(i=k;i<n-1;i+=Span)/*這個for之後的是「組內採用直接插入法排序」*/
{
s=x[i+Span];
j=i;
while(j>-1&&s.key<x[j].key)
{
x[j+Span]=x[j];
j-=Span;
}
x[j+Span]=s;
}
}
}
}
----------------------------
直接選擇排序
說明:每次將後面的最小的找出來插入前面的已排好的序中。同理,具有n個記錄的序列要做n-1次排序。
時間復雜度為O(n2)。
void SelectSort(elemtype x[],int n)
/*用直接選擇排序法對x[0]-x[n-1]排序*/
{
int i,j,Small;
elemtype Temp;
for(i=0;i<n-1;i++)
{
Small=i;
for(j=i+1;j<n;j++)
if(x[j].key<x[Small].key)
Small=j;
if(Small!=i)
{
Temp=x[i];
x[i]=x[Small];
x[Small]=Temp;
}
}
}
--------------------------
冒泡排序
說明:兩個兩個比較,將大的往後移。通過第一次冒泡排序,使得待排序的n個記錄中關鍵字最大的記錄排到
了序列的最後一個位置上。然後對序列中前n-1個記錄進行第二次冒泡排序。。。對於n個記錄的序列,共需進
行n次冒泡排序。時間復雜度為O(n2)。
void BubbleSort(elemtype x[],int n)
/*用冒泡排序法對x[0]-x[n-1]排序*/
{
int i,j,flag=1;
elemtype Temp;
for(i=1;i<n&&flag==1;i++)
{
flag=0;
for(j=0;j<n-i;j++)
{
if(x[j].key>x[j+1].key)
{
flag=1;
Temp=x[j];
x[j]=x[j+1];
x[j+1]=Temp;
}
}
}
}
-----------------------------
快速排序
說明:又叫分區交換排序,是對冒泡排序方法的一種改進。時間復雜度為O(nlog2n)。
void QuickSort(elemtype x[],int low,int high)
/*用遞歸方法對記錄x[0]-x[n-1]進行快速排序*/
{
int i,j;
elemtype Temp;
i=low;
j=high;
Temp=x[low];
while(i<j)
{
/*在序列的右端掃描*/
while(i<j&&Temp.key<=x[j].key)j--;
if(i<j)
{
x[i]=x[j];
i++;
}
/*在序列的左端掃描*/
while(i<j&&x[i].key<Temp.key)i++;
if(i<j)
{
x[j]=x[i];
j--;
}
}
x[i]=Temp;
/*對子序列進行快速排序*/
if(low<i-1)QuickSort(x,low,i-1);
if(j+1<high)QuickSort(x,j+1,high);
}
-------------------------
歸並排序
說明:所謂歸並排序就是將兩個或兩個以上的有序數據序列合並成一個有序數據序列的過程。
時間復雜度為O(nlog2n)。
void merge(r,l,m,h,r1,r2)/*r[l,m]及r[m+1,h]分別有序,歸並後置於r2中*/
sqlist r,r2;
int l,m,h;
{
int i,j,k;
k=l;/*k是r2的指示器,i、j分別為s1、s2的指示器*/
i=l;
j=m+1;
while(i<=m&&j<=h)
{
if(r[i].key<=r[j].key)
{
r2[k]=r[i];
i++;
}
else
{
r2[k]=r[j];
j++;
}
k++;
}
if(i>m) /*s1結束*/
while(j<=h)
{
r2[k]=r[j];
j++;k++;
}
else
while(i<=m)
{
r2[k]=r[i];
i++;k++;
}
}