1、需求分析
(1)輸入資料的形式為:偽隨機數產生程式產生,且每次輸入數不少於100個,至少要用5組不同的輸入資料
(2)輸出的形式為:輸出關鍵字參加的比較次數和關鍵字的移動次數(關鍵字交換計為3次移動)的資料
(3)程式能達到的功能:對起泡排序,直接插入排序,簡單選擇排序,快速排序,希爾排序,堆排序這6種常用的內部排序演算法進行比較,比較的指標為有關鍵字參加的比較次數和關鍵字的移動次數(關鍵字交換計為3次移動)
(4)測試資料:正確輸入為由偽隨機數產生程式產生100個隨機數,然後輸出比較結果,錯誤輸入為輸入極少量資料,此時輸出結果不能比較
(5)C語言編寫
2、概要設計
本程式設計了一個順序表結構來儲存需要排序的資料,主程式運行之後,先初始化6個順序表,然後就進入一個需要迴圈5次的大迴圈裡,在迴圈裡面有一個小迴圈需要執行100次產生100個隨機數給6個順序表,此時這6個順序表的資料相同且是隨機數,然後分別調用void InsertSort(SqList *L)冒泡排序,void InsertSort(SqList *L)直接插入排序,void SelectSort(SqList *L)簡單選擇排序,void QuickSort(SqList *L) 快速排序,void ShellSort(SqList *L)希爾排序,void HeapSort(SqList *L)堆排序,來對待排序列排序。其中void InsertSort(SqList *L),void SelectSort(SqList *L),void HeapSort(SqList *L)這幾個模組中,當兩個資料需要交換位置時調用了void swap(SqList *L,int i,int j)模組。void HeapSort(SqList *L) 模組中調用了void HeapAdjust(SqList *L,int s,int m,int &a,int &b),使得將L->r[1...i-1]重新調整為大頂堆 。void QuickSort(SqList *L) 模組中調用了void QSort(SqList *L,int low,int high,int &k,int &l)來對對順序表L中的子序列L->r[low..high]作快速排序,void QSort(SqList *L,int low,int high,int &k,int &l)中又調用了int Partition(SqList *L,int low,int high,int &k,int &l)來將L->r[low..high]一分為二,算出樞軸值。
3、詳細設計
順序表結構裡有用於int類型的儲存排序資料的數組,r[0]用作哨兵或臨時變數,以及int類型的用於記錄順序表的長度變數。
typedef struct{
int r[MAXSIZE+1];
int length;
}SqList;
4、調試分析
(1)調試過程中遇到的問題:
1、一開始產生隨機數時,對順序表的數組裡都賦了值,結果出來時,第一個數會沒有排序,其它的數都正常,原因是r[0]是用作哨兵或者存放臨時變數,所以一開始賦值時,r[0]不應該賦值。
2、計算關鍵字交換的次數時,定義的變數為int l;計算結果出來之後,數字非常大。是因為對於局部變數,不賦初值的話,其實它裡面存的是一個隨機的值,而並不是0,所以定義時應定義為int l = 0;
3、在計算關鍵字的比較和交換時,由於模組之間要相互調用,所以計算的值要當做參數傳輸,但返回時不能返回兩個傳回值,困擾了較久,然後用了int &k,int &l,即可以改變實參的方法就解決了問題
(2)演算法的時空分析:
排序法 平均時間 最差情形 穩定度 額外空間 備忘
冒泡 O(n2) O(n2) 穩定 O(1) n小時較好
選擇 O(n2) O(n2) 不穩定 O(1) n小時較好
插入 O(nlogn) O(n2) 穩定 O(1) 大部分已排序時較好
希爾 O(n^1.5) 不詳 不穩定 O(1)
快速 O(nlogn) O(n2) 不穩定 O(nlogn) n大時較好
堆 O(nlogn) O(nlogn) 不穩定 O(1) n大時較好
冒泡排序最佳化:當序列還沒比較但已經有序時,其實已經不要再繼續後面的迴圈判斷工作了,所以增加一個標量flag來實現這演算法的改進
void BubbleSort2(SqList *L){
int i,j;
Status flag = TRUE;
for(i = 1;i<L->length && flag;i++){
flag = FALSE;
for(j = L->length;j>=i;j--){
if(L->r[j] > L->r[j+1]){
swap(L,j,j+1);
flag = TRUE;
}
}
}
}
(3)經驗與體會:開始打代碼前要理一下思想,找到最佳入口來寫代碼,模組之間的調用也要先構思好,不然後面代碼之間過於混亂,當出現bug時,會很難找到,要充分利用調試的功能
5、使用者使用說明
本程式中要使用者輸入100個數是不太可能的。所以本程式執行之後就會自動產生隨機數。直接比較結果就會自動顯示。使用簡單,直接運行就行。
6、測試結果
由測試的資料可以看到冒泡排序和簡單排序中關鍵字的比較次數相同且不會波動,這是由於無論資料的數的變化,只要總數不變,冒泡排序和簡單排序都要執行迴圈中的比較語句。簡單排序中關鍵字的移動自述最少且波動幅度不大,是由於直接插入排序是將記錄從無序區直接插入到有序區,所以沒有資料之間的交換,所以移動次數較少,但是比較次數較多。堆排序中比較次數和移動次數兩者相差不大,是由於堆排序中將是頻繁將最大值與末尾比較然後交換。希爾排序和快排中關鍵字的移動次數波動較大。是由於這兩種排序進行資料之間交換位置的動作較大,因此當資料較混亂和較整齊時,移動次數的結果會相差較大。
5、附錄
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 100
//排序用的順序表結構
typedef struct{
int r[MAXSIZE+1];//用於儲存要排序數組,r[0]用做哨兵或臨時變數
int length;
}SqList;
//交換兩個值
void swap(SqList *L,int i,int j){
int temp = L->r[i];
L->r[i] = L->r[j];
L->r[j] = temp;
}
//冒泡排序
void BubbleSort(SqList *L){
int i,j,k=0,l=0;
for (i = 1;i<L->length;i++){//外層迴圈,確定所有數都與其它數比較
//k++;
for(j = i+1;j<=L->length;j++){//內層迴圈,用一個數跟其它數比較大小
k++;
if(L->r[i] > L->r[j]){
swap(L,i,j);
l = l+3;
}
}
}
printf("冒泡排序中關鍵字的比較次數為%d:",k);
printf("\n冒泡排序中關鍵字的移動次數為%d:",l);
printf("\n");
}
//直接排序
void InsertSort(SqList *L) {
int i,j,k=0,l=0;
for(i = 2;i<=L->length;i++){
k++;
if(L->r[i] < L->r[i-1]){
L->r[0] = L->r[i];//設定哨兵
l++;
for(j = i-1;L->r[j] > L->r[0];j--){
L->r[j+1] = L->r[j];//記錄後移
l++;
k++;
}
k++;//這一步容易忽略,跳出迴圈的時候,是比較了一次,不符合條件才跳出的
L->r[j+1] = L->r[0];//插入到正確位置
l++;
}
}
printf("直接排序中關鍵字的比較次數為%d:",k);
printf("\n直接排序中關鍵字的移動次數為%d:",l);
printf("\n");
}
//簡單選擇排序
void SelectSort(SqList *L){
int i,j,min;
int k=0,l=0;
for(i = 1;i<L->length;i++){
//k++;
min = i;
for(j = i+1;j<=L->length;j++){
k++;
if(L->r[min] > L->r[j]){
min = j;
}
}
if(i != min){//判斷 i!min,則證明有資料比 r[min]還要小,則需交換
swap(L,i,min);
l = l+3;
}
}
printf("簡單排序中關鍵字的比較次數為:%d",k);