Ⅰ 插入排序的演算法復雜度
如果目標是把n個元素的序列升序排列,那麼採用插入排序存在最好情況和最壞情況。最好情況就是,序列已經是升序排列了,在這種情況下,需要進行的比較操作需(n-1)次即可。最壞情況就是,序列是降序排列,那麼此時需要進行的比較共有n(n-1)/2次。插入排序的賦值操作是比較操作的次數加上 (n-1)次。平均來說插入排序演算法的時間復雜度為O(n^2)。因而,插入排序不適合對於數據量比較大的排序應用。但是,如果需要排序的數據量很小,例如,量級小於千,那麼插入排序還是一個不錯的選擇。
Ⅱ 插入排序的分類
包括:直接插入排序,二分插入排序(又稱折半插入排序),鏈表插入排序,希爾排序(又稱縮小增量排序)。屬於穩定排序的一種(通俗地講,就是兩個相等的數不會交換位置) 。 直接插入排序是一種簡單的插入排序法,其基本思想是:把待排序的紀錄按其關鍵碼值的大小逐個插入到一個已經排好序的有序序列中,直到所有的紀錄插入完為止,得到一個新的有序序列。
例如,已知待排序的一組紀錄是:
60,71,49,11,24,3,66
假設在排序過程中,前3個紀錄已按關鍵碼值遞增的次序重新排列,構成一個有序序列:
49,60,71
將待排序紀錄中的第4個紀錄(即11)插入上述有序序列,以得到一個新的含4個紀錄的有序序列。首先,應找到11的插入位置,再進行插入。可以講11放入數組的第一個單元r[0]中,這個單元稱為監視哨,然後從71起從右到左查找,11小於71,將71右移一個位置,11小於60,又將60右移一個位置,11小於49,又再將49右移一個位置,這時再將11與r[0]的值比較,11≥r[0],它的插入位置就是r[1]。假設11大於第一個值r[1]。它的插入位置應該在r[1]和r[2]之間,由於60已經右移了,留出來的位置正好留給11.後面的紀錄依照同樣的方法逐個插入到該有序序列中。若紀錄數n,續進行n-1趟排序,才能完成。
直接插入排序的演算法思路:
(1) 設置監視哨r[0],將待插入紀錄的值賦值給r[0];
(2) 設置開始查找的位置j;
(3) 在數組中進行搜索,搜索中將第j個紀錄後移,直至r[0].key≥r[j].key為止;
(4) 將r[0]插入r[j+1]的位置上。
直接插入排序演算法:
public void zjinsert (Redtype r[],int n)
{
int I,j;
Redtype temp;
for (i=1;i<n;i++)
{
temp = r[i];
j=i-1;
while (j>-1 &&temp.key<r[j].key)
{
r[j+1]=r[j];
j--;
}
r[j+1]=temp;
}
} 將直接插入排序中尋找A[i]的插入位置的方法改為採用折半比較,即可得到折半插入排序演算法。在處理A[i]時,A[0]……A[i-1]已經按關鍵碼值排好序。所謂折半比較,就是在插入A[i]時,取A[i-1/2]的關鍵碼值與A[i]的關鍵碼值進行比較,如果A[i]的關鍵碼值小於A[i-1/2]的關鍵碼值,則說明A[i]只能插入A[0]到A[i-1/2]之間,故可以在A[0]到A[i-1/2-1]之間繼續使用折半比較;否則只能插入A[i-1/2]到A[i-1]之間,故可以在A[i-1/2+1]到A[i-1]之間繼續使用折半比較。如此擔負,直到最後能夠確定插入的位置為止。一般在A[k]和A[r]之間採用折半,其中間結點為A[k+r/2],經過一次比較即可排除一半紀錄,把可能插入的區間減小了一半,故稱為折半。執行折半插入排序的前提是文件紀錄必須按順序存儲。
折半插入排序的演算法思想:
演算法的基本過程:
(1)計算 0 ~ i-1 的中間點,用 i 索引處的元素與中間值進行比較,如果 i 索引處的元素大,說明要插入的這個元素應該在中間值和剛加入i索引之間,反之,就是在剛開始的位置 到中間值的位置,這樣很簡單的完成了折半;
(2)在相應的半個范圍裡面找插入的位置時,不斷的用(1)步驟縮小范圍,不停的折半,范圍依次縮小為 1/2 1/4 1/8 .......快速的確定出第 i 個元素要插在什麼地方;
(3)確定位置之後,將整個序列後移,並將元素插入到相應位置。
3 希爾排序法
希爾排序法又稱縮小增量法。希爾排序法的基本思想是:先選定一個整數,把待排序文件中所有記錄分成個組,所有距離為的記錄分在同一組內,並對每一組內的記錄進行排序。然後,取,重復上述分組和排序的工作。當到達=1時,所有記錄在統一組內排好序。
各組內的排序通常採用直接插入法。由於開始時s的取值較大,每組內記錄數較少,所以排序比較快。隨著不斷增大,每組內的記錄數逐步增多,但由於已經按排好序,因此排序速度也比較快。
Ⅲ 面試必會八大排序演算法(Python)
一、插入排序
介紹
插入排序的基本操作就是將一個數據插入到已經排好序的有序數據中,從而得到一個新的、個數加一的有序數據。
演算法適用於少量數據的排序,時間復雜度為O(n^2)。
插入排演算法是穩定的排序方法。
步驟
①從第一個元素開始,該元素可以認為已經被排序
②取出下一個元素,在已經排序的元素序列中從後向前掃描
③如果該元素(已排序)大於新元素,將該元素移到下一位置
④重復步驟3,直到找到已排序的元素小於或者等於新元素的位置
⑤將新元素插入到該位置中
⑥重復步驟2
排序演示
演算法實現
二、冒泡排序
介紹
冒泡排序(Bubble Sort)是一種簡單的排序演算法,時間復雜度為O(n^2)。
它重復地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數列的工作是重復地進行直到沒有再需要交換,也就是說該數列已經排序完成。
這個演算法的名字由來是因為越小的元素會經由交換慢慢「浮」到數列的頂端。
原理
循環遍歷列表,每次循環找出循環最大的元素排在後面;
需要使用嵌套循環實現:外層循環控制總循環次數,內層循環負責每輪的循環比較。
步驟
①比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。
②對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對。在這一點,最後的元素應該會是最大的數。
③針對所有的元素重復以上的步驟,除了最後一個。
④持續每次對越來越少的元素重復上面的步驟,直到沒有任何一對數字需要比較。
演算法實現:
三、快速排序
介紹
快速排序(Quicksort)是對冒泡排序的一種改進,借用了分治的思想,由C. A. R. Hoare在1962年提出。
基本思想
快速排序的基本思想是:挖坑填數 + 分治法。
首先選出一個軸值(pivot,也有叫基準的),通過一趟排序將待排記錄分隔成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序。
實現步驟
①從數列中挑出一個元素,稱為 「基準」(pivot);
②重新排序數列,所有元素比基準值小的擺放在基準前面,所有元素比基準值大的擺在基準的後面(相同的數可以到任一邊);
③對所有兩個小數列重復第二步,直至各區間只有一個數。
排序演示
演算法實現
四、希爾排序
介紹
希爾排序(Shell Sort)是插入排序的一種,也是縮小增量排序,是直接插入排序演算法的一種更高效的改進版本。希爾排序是非穩定排序演算法,時間復雜度為:O(1.3n)。
希爾排序是基於插入排序的以下兩點性質而提出改進方法的:
·插入排序在對幾乎已經排好序的數據操作時, 效率高, 即可以達到線性排序的效率;
·但插入排序一般來說是低效的, 因為插入排序每次只能將數據移動一位。
基本思想
①希爾排序是把記錄按下標的一定量分組,對每組使用直接插入演算法排序;
②隨著增量逐漸減少,每組包1含的關鍵詞越來越多,當增量減至1時,整個文件恰被分成一組,演算法被終止。
排序演示
演算法實現
五、選擇排序
介紹
選擇排序(Selection sort)是一種簡單直觀的排序演算法,時間復雜度為Ο(n2)。
基本思想
選擇排序的基本思想:比較 + 交換。
第一趟,在待排序記錄r1 ~ r[n]中選出最小的記錄,將它與r1交換;
第二趟,在待排序記錄r2 ~ r[n]中選出最小的記錄,將它與r2交換;
以此類推,第 i 趟,在待排序記錄ri ~ r[n]中選出最小的記錄,將它與r[i]交換,使有序序列不斷增長直到全部排序完畢。
排序演示
選擇排序的示例動畫。紅色表示當前最小值,黃色表示已排序序列,藍色表示當前位置。
演算法實現
六、堆排序
介紹
堆排序(Heapsort)是指利用堆積樹(堆)這種數據結構所設計的一種排序演算法,它是選擇排序的一種。
利用數組的特點快速指定索引的元素。
基本思想
堆分為大根堆和小根堆,是完全二叉樹。
大根堆的要求是每個節點的值不大於其父節點的值,即A[PARENT[i]] >=A[i]。
在數組的非降序排序中,需要使用的就是大根堆,因為根據大根堆的要求可知,最大的值一定在堆頂。
排序演示
演算法實現
七、歸並排序
介紹
歸並排序(Merge sort)是建立在歸並操作上的一種有效的排序演算法。該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用。
基本思想
歸並排序演算法是將兩個(或兩個以上)有序表合並成一個新的有序表,即把待排序序列分為若干個子序列,每個子序列是有序的。然後再把有序子序列合並為整體有序序列。
演算法思想
自上而下遞歸法(假如序列共有n個元素)
① 將序列每相鄰兩個數字進行歸並操作,形成 floor(n/2)個序列,排序後每個序列包含兩個元素;
② 將上述序列再次歸並,形成 floor(n/4)個序列,每個序列包含四個元素;
③ 重復步驟②,直到所有元素排序完畢。
自下而上迭代法
① 申請空間,使其大小為兩個已經排序序列之和,該空間用來存放合並後的序列;
② 設定兩個指針,最初位置分別為兩個已經排序序列的起始位置;
③ 比較兩個指針所指向的元素,選擇相對小的元素放入到合並空間,並移動指針到下一位置;
④ 重復步驟③直到某一指針達到序列尾;
⑤ 將另一序列剩下的所有元素直接復制到合並序列尾。
排序演示
演算法實現
八、基數排序
介紹
基數排序(Radix Sort)屬於「分配式排序」,又稱為「桶子法」。
基數排序法是屬於穩定性的排序,其時間復雜度為O (nlog(r)m) ,其中 r 為採取的基數,而m為堆數。
在某些時候,基數排序法的效率高於其他的穩定性排序法。
基本思想
將所有待比較數值(正整數)統一為同樣的數位長度,數位較短的數前面補零。然後,從最低位開始,依次進行一次排序。這樣從最低位排序一直到最高位排序完成以後,數列就變成一個有序序列。
基數排序按照優先從高位或低位來排序有兩種實現方案:
MSD(Most significant digital) 從最左側高位開始進行排序。先按k1排序分組, 同一組中記錄, 關鍵碼k1相等,再對各組按k2排序分成子組, 之後, 對後面的關鍵碼繼續這樣的排序分組, 直到按最次位關鍵碼kd對各子組排序後. 再將各組連接起來,便得到一個有序序列。MSD方式適用於位數多的序列。
LSD (Least significant digital)從最右側低位開始進行排序。先從kd開始排序,再對kd-1進行排序,依次重復,直到對k1排序後便得到一個有序序列。LSD方式適用於位數少的序列。
排序效果
演算法實現
九、總結
各種排序的穩定性、時間復雜度、空間復雜度的總結:
平方階O(n²)排序:各類簡單排序:直接插入、直接選擇和冒泡排序;
從時間復雜度來說:
線性對數階O(nlog₂n)排序:快速排序、堆排序和歸並排序;
O(n1+§))排序,§是介於0和1之間的常數:希爾排序 ;
線性階O(n)排序:基數排序,此外還有桶、箱排序。
Ⅳ 順序表的插入演算法的實現
#include<stdio.h>
#include<stdlib.h>
#define LIST_INIT_SIZE 100
#define LISTINCREMENT 10
typedef int status ;
typedef int ElemType ;
typedef struct{
ElemType *elem;
int length,listsize;
}SqList;
status InitList(SqList &L)//初始化
{
L.elem=(int *)malloc(100*sizeof(int));
if(!L.elem) exit(-2);
L.listsize=100;
L.length=0;
return 1;
}
/*先建立新表*/
status Build(SqList &L)
{
int i,n;
printf("請輸入元素個數n和n個元素\n");
scanf("%d",&n);
//if(n>LIST_INIT_SIZE)
for(i=0;i<n;i++)
scanf("%d",L.elem+i);
L.length=n;
return 1;
}
/*輸出表中元素和長度*/
void Print(SqList &L)
{
int i;
for(i=0;i<L.length;i++)
printf("%d ",*(L.elem+i));
printf("\n長度為:%d\n\n",L.length);
}
/*刪除值為X的元素*/
status ListDelete1(SqList &L,int x)
{
int i;
for(i=0;i<L.length;i++)
if(*(L.elem+i)==x)
break;
if(i==L.length)
return 0;
for(i++;i<L.length;i++)
*(L.elem+i-1)=*(L.elem+i);
L.length--;
return 1;
}
/*逆置函數*/
void Inverse(SqList &L)
{
int i,t;
for(i=0;i<L.length/2;i++)
{
t=*(L.elem+i);
*(L.elem+i)=*(L.elem+L.length-i-1);
*(L.elem+L.length-i-1)=t;
}
printf("表逆置成功!!!\n");
}
/*(升序)*/
void Sort(SqList &L)
{
int i,j,t;
for(i=1;i<L.length;i++)
for(j=0;j<L.length-i;j++)
{
if(*(L.elem+j)>*(L.elem+j+1))
{
t=*(L.elem+j);
*(L.elem+j)=*(L.elem+j+1);
*(L.elem+j+1)=t;
}
}
printf("已升序\n");
}
/*合並兩個線性表*/
status Merger(SqList &L,SqList &Lb)
{
int i,j,k;
SqList Lc;
InitList(Lc);
if(Lc.listsize<L.length+Lb.length)
{
Lc.elem=(ElemType *)realloc(Lc.elem,(L.length+Lb.length+LISTINCREMENT)*sizeof(ElemType));
if(!L.elem) exit(-2);
Lc.listsize=L.length+Lb.length+LISTINCREMENT;
}
i=j=k=0;
while(i<L.length && j<Lb.length)
{
if(*(L.elem+i) < *(Lb.elem+j))
{
*(Lc.elem+k)=*(L.elem+i);
k++;i++;
}
else
{
*(Lc.elem+k)=*(Lb.elem+j);
k++;j++;
}
}
while(i<L.length)
{
*(Lc.elem+k)=*(L.elem+i);
k++;i++;
}
while(j<Lb.length)
{
*(Lc.elem+k)=*(Lb.elem+j);
k++;j++;
}
Lc.length=L.length+Lb.length;
L=Lc;
return 1;
}
/*將X插入,使仍然有序*/
status ListInsert(SqList &L,int x)
{
int i,k;
if(L.length>=L.listsize)
{
L.elem=(ElemType *)realloc(L.elem,(L.listsize+LISTINCREMENT)*sizeof(ElemType));
if(!L.elem) exit(-2);
L.listsize+=LISTINCREMENT;
}
for(i=0;i<L.length;i++)
if(x<*(L.elem+i))
break;
k=i;
for(i=L.length;i>k;i--)
*(L.elem+i)=*(L.elem+i-1);
*(L.elem+k)=x;
L.length++;
return 1;
}
/*提示函數*/
void Tips()
{
printf("請選擇你的想要的操作:\n");
printf("<1> 輸出順序表及順序表的長度\n");
printf("<2> 刪除值為x的結點\n");
printf("<3> 將順序表逆置\n");
printf("<4> 將順序表按升序排序\n");
printf("<5> 將x插入到順序表的適當位置上\n");
printf("<6> 將兩個有序表合並\n");
printf("<0> 退出\n\n");
}
int main()
{
SqList L,Lb;
InitList(L);
Build(L);
int a,x,flag;
//SqList L,Lb;
Tips();
scanf("%d",&a);
while(a)
{
switch(a)
{
case 1:
{ Print(L);
break;}
case 2:
{ printf("請輸入要刪除的數據X:\n");
scanf("%d",&x);
flag=ListDelete1(L,x);
if(flag)
printf("刪除成功!!\n\n");
else
printf("元素不存在,刪除失敗!!\n\n");
break;}
case 3:
Inverse(L);
break;
case 4:
Sort(L);
break;
case 5:
printf("請輸入要插入的數據X:\n");
scanf("%d",&x);
flag=ListInsert(L,x);
if(flag)
printf("插入成功!!\n\n");
else
printf("插入失敗!!\n\n");
break;
case 6:
printf("請輸入Lb的內容:\n");
InitList(Lb);
Build(Lb);
flag=Merger(L,Lb);
if(flag)
printf("合並成功!!\n\n");
break;
//default;
Tips();
scanf("%d",&a);
}
}
return 0;
}
Ⅳ 10種排序演算法
排序演算法是《數據結構與演算法》中最基本的演算法之一。
排序演算法可以分為內部排序和外部排序,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大,一次不能容納全部的排序記錄,在排序過程中需要訪問外存。常見的內部排序演算法有:插入排序、希爾排序、選擇排序、冒泡排序、歸並排序、快速排序、堆排序、基數排序等。用一張圖概括:
點擊以下圖片查看大圖:
關於時間復雜度
平方階 (O(n2)) 排序 各類簡單排序:直接插入、直接選擇和冒泡排序。
線性對數階 (O(nlog2n)) 排序 快速排序、堆排序和歸並排序;
O(n1+§)) 排序,§ 是介於 0 和 1 之間的常數。 希爾排序
線性階 (O(n)) 排序 基數排序,此外還有桶、箱排序。
關於穩定性
穩定的排序演算法:冒泡排序、插入排序、歸並排序和基數排序。
不是穩定的排序演算法:選擇排序、快速排序、希爾排序、堆排序。
名詞解釋:
n:數據規模 k:"桶"的個數 In-place:佔用常數內存,不佔用額外內存 Out-place:佔用額外內存 穩定性:排序後 2 個相等鍵值的順序和排序之前它們的順序相同包含以下內容:
1、冒泡排序 2、選擇排序 3、插入排序數搭 4、希爾排序 5、歸並排序 6、快速排序 7、堆排序 8、計數排序 9、桶排序 10、基數排序排序演算法包含的相關內容具體如下:
冒泡排序演算法
冒泡排序(Bubble Sort)也是一種簡單直觀的排序演算法。它重復地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數列的工作是重復地進行直到沒有再需要交換,也就是說該薯畝拿數列已經排序完成。這個演算法的名字由來是因為越小的元素會經由交換慢慢"浮"到數列的頂端。
選擇排序演算法
選擇排序是一種簡單直觀的排序演算法,耐差無論什麼數據進去都是 O(n?) 的時間復雜度。所以用到它的時候,數據規模越小越好。唯一的好處可能就是不佔用額外的內存空間。
插入排序演算法
插入排序的代碼實現雖然沒有冒泡排序和選擇排序那麼簡單粗暴,但它的原理應該是最容易理解的了,因為只要打過撲克牌的人都應該能夠秒懂。插入排序是一種最簡單直觀的排序演算法,它的工作原理是通過構建有序序列,對於未排序數據,在已排序序列中從後向前掃描,找到相應位置並插入。
希爾排序演算法
希爾排序,也稱遞減增量排序演算法,是插入排序的一種更高效的改進版本。但希爾排序是非穩定排序演算法。
歸並排序演算法
歸並排序(Merge sort)是建立在歸並操作上的一種有效的排序演算法。該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用。
快速排序演算法
快速排序是由東尼·霍爾所發展的一種排序演算法。在平均狀況下,排序 n 個項目要 Ο(nlogn) 次比較。在最壞狀況下則需要 Ο(n2) 次比較,但這種狀況並不常見。事實上,快速排序通常明顯比其他 Ο(nlogn) 演算法更快,因為它的內部循環(inner loop)可以在大部分的架構上很有效率地被實現出來。
堆排序演算法
堆排序(Heapsort)是指利用堆這種數據結構所設計的一種排序演算法。堆積是一個近似完全二叉樹的結構,並同時滿足堆積的性質:即子結點的鍵值或索引總是小於(或者大於)它的父節點。堆排序可以說是一種利用堆的概念來排序的選擇排序。
計數排序演算法
計數排序的核心在於將輸入的數據值轉化為鍵存儲在額外開辟的數組空間中。作為一種線性時間復雜度的排序,計數排序要求輸入的數據必須是有確定范圍的整數。
桶排序演算法
桶排序是計數排序的升級版。它利用了函數的映射關系,高效與否的關鍵就在於這個映射函數的確定。
基數排序演算法
基數排序是一種非比較型整數排序演算法,其原理是將整數按位數切割成不同的數字,然後按每個位數分別比較。由於整數也可以表達字元串(比如名字或日期)和特定格式的浮點數,所以基數排序也不是只能使用於整數。
Ⅵ 鏈表的插入排序演算法
鏈表是一種物理存儲單元上非連續、非順序的存儲結構,數據元素的邏輯順序是通過鏈表中的指針鏈接次序實現的。鏈表由一系列結點(鏈表中每一個元素稱為結點)組成,結點可以在運行時動態生成。每個結點包括兩個部分:一個是存儲數據元素的數據域,另一個是存儲下一個結點地址的指針域。 相比於線性表順序結構,操作復雜。由於不必須按順序存儲,鏈表在插入的時候可以達到O(1)的復雜度,比另一種線性表順序錶快得多,但是查找一個節點或者訪問特定編號的節點則需要O(n)的時間,而線性表和順序表相應的時間復雜度分別是O(logn)和O(1)。
使用鏈表結構可以克服數組鏈表需要預先知道數據大小的缺點,鏈表結構可以充分利用計算機內存空間,實現靈活的內存動態管理。但是鏈表失去了數組隨機讀取的優點,同時鏈表由於增加了結點的指針域,空間開銷比較大。鏈表最明顯的好處就是,常規數組排列關聯項目的方式可能不同於這些數據項目在記憶體或磁碟上順序,數據的存取往往要在不同的排列順序中轉換。鏈表允許插入和移除表上任意位置上的節點,但是不允許隨機存取。鏈表有很多種不同的類型:單向鏈表,雙向鏈表以及循環鏈表。鏈表可以在多種編程語言中實現。像Lisp和Scheme這樣的語言的內建數據類型中就包含了鏈表的存取和操作。程序語言或面向對象語言,如C,C++和Java依靠易變工具來生成鏈表。
Ⅶ c語言插入法排序的演算法步驟
演算法描述
一般來說,插入排序都採用in-place在數組上實現。具體演算法描述如下:
從第一個元素開始,該元素可以認為已經被排序
取出下一個元素,在已經排序的元素序列中從後向前掃描
如果該元素(已排序)大於新元素,將該元素移到下一位置
重復步驟3,直到找到已排序的元素小於或者等於新元素的位置
將新元素插入到該位置後
重復步驟2~5
如果比較操作的代價比交換操作大的話,可以採用二分查找法來減少比較操作的數目。該演算法可以認為是插入排序的一個變種,稱為二分查找排序。
范常式式碼
void insertion_sort(int array[], int first, int last)
{
int i,j;
int temp;
for (i = first+1; i<=last;i++)
{
temp = array[i];
j=i-1;
while((j>=first) && (array[j] > temp))
{
array[j+1] = array[j];
j--;
}
array[j+1] = temp;
}
}