數據結構與算法(C/C++版)【排序】

第八章《排序》算法

 1、直接插入排序 shell

//直接插入排序 //算法思想:每趟將一個待排的關鍵字按照其值的大小插入到已經排好的部分有序序列的適當位置上,直到全部待排關鍵字都被插入到有序序列中爲止。

 //(1)時間複雜度分析:
 //        ①最壞狀況(整個序列逆序):O(n²)
 //        ②最好狀況(整個序列有序):O(n)
 //        ③平均時間複雜度:O(n²)
 //(2)空間複雜度分析:
 //        ①:O(1)數組

void InsertSort(int R[], int n)   //待排關鍵字存儲在R[]中,默認爲整型,個數爲n
{ int i,j; int temp; for(int i= 1; i<n; i++)       //無序列表中挑選元素
 { temp=R[i];                //將帶插入關鍵字暫存於temp中
        j=i-1; while(j>0 && temp < R[j]) //循環完成從待排關鍵字以前的關鍵字開始掃描,若是大於待排關鍵字,則後移一位
 { R[j+1] = R[j]; --j; } R[j+1] = temp;            //將temp中暫存的待排關鍵字插入到正確位置
 } }

 

 2、折半插入排序 函數

//折半插入排序 //算法思想:數據自己有序的前提下,取數據中間的元素與待插入元素進行對比,若待插入元素大於中間元素,則右半部分重複相同查找插入操做;若待插入元素小於中間元素,則左半部分重複相同查找插入操做;以此類推,直到插入待插入元素。 //(1)時間複雜度分析: // ①最壞狀況(整個序列逆序):O(n²) // ②最好狀況(整個序列有序):O(nlog₂n) // ③平均時間複雜度:O(n²) //(2)空間複雜度分析: // ①:O(1)
void BInsertSort(int a[],int size) { int i,j,low = 0,high = 0,mid; int temp = 0; for (i=1; i<size; i++) { low=0; high=i-1; temp=a[i]; //採用折半查找法判斷插入位置,最終變量 low 表示插入位置
        while (low<=high) { mid=(low+high)/2; if (a[mid]>temp) { high=mid-1; } else { low=mid+1; } } //有序表中插入位置後的元素統一後移
        for (j=i; j>low; j--) { a[j]=a[j-1]; } a[low]=temp;//插入元素
 } }

 

 3、希爾排序 ui

//希爾排序 //算法思想:以增量來分割整個序列,將分割的各組進行直接插入排序,而後繼續相同操做直到最後以增量1分割整個序列,其實就是對整個序列進行一趟直接插入排序,從而完成整個希爾排序。 //(1)時間複雜度分析:與增量的選取有關 // ①希爾本身提出的(每次除以2,並向下取整):O(n²) // ②2^k+1,且小於待排序列長度:O(n^1.5) // ③選取增量需注意:1.增量序列的最後一個值必定取1 // 2.增量序列中的值應儘可能沒有除1以外的公因子 //(2)空間複雜度分析: // ①:O(1)
viod shellSort(int arr[],int n) { int temp; for(int gap=n/2;gap>0;gap/=2) { for(int i=gap;i<n;++i) { temp=arr[i]; int j; for(j=i;j>=gap && arr[j-gap]>temp;j-=gap) //每一個元素與本身組內的數據進行直接插入排序 
                arr[j]=arr[j-gap]; arr[j]=temp; } } }

 

 4、冒泡排序 spa

//冒泡排序 //算法思想:【把數據降序】第一個元素和第二個元素比較,若果第一個大則兩者交換,不然不交換;而後第二個元素和第三個元素比較。。。。以此類推,最終最大的元素被換到了最後面,完成一趟冒泡排序,通過多躺這樣的排序,最終整個序列完成降序操做。 //結束標誌是:一趟排序過程當中沒有發生元素交換。 //(1)時間複雜度分析: // ①最壞狀況(整個序列逆序):O(n²) // ②最好狀況(整個序列有序):O(n) // ③平均時間複雜度:O(n²) //(2)空間複雜度分析: // ①:O(1)

void BubbleSort(int R[],int n)   //默認待排序元素爲整型
{ int i,j; int flag; int temp; for(i=n-1;i>=1;--i)          //無序序列範圍(逐漸較少)
 { flag=0;                  //變量flag用來標記本趟排序是否放生了交換
        for(j=1;j<=i;++j)        //交換。若爲0,前面沒有元素,無心義
            if(R[j-1]>R[j]) { temp=R[j]; R[j]=R[j-1]; R[j-1]=temp; flag=1; } if(flag==0) return; } }

 

5、快速排序 code

//快速排序 //算法思想:①取第一個元素P(此時留下一個空位),先從右往左找比P元素的小的元素,找到後把該元素放到左邊的空位(此時右邊留下一個空位),而後在從左往右找比P元素大的元素,找到後把該元素放到右邊的空位(此時左邊又留下一個空位),以此類推,使元素p歸位; // ②列表被p分紅兩部分,左邊都比p小,右邊都比p大; // ③P兩側用一樣的方式遞歸完成最終的排序。 //(1)時間複雜度分析: // ①最壞狀況(整個序列逆序):O(n²) // ②最好狀況(整個序列有序):O(nlog2₂n) // ③平均時間複雜度:O(nlog2₂n) //(2)空間複雜度分析: // ①:O(log2₂n):遞歸須要棧的輔助
 viod QuickSort(int R[],int low,int high) //對從R[low]到R[high]的元素進行排序
{ int temp; int i=low,j=high; if(low<high) { temp=R[low]; while(i!=j)  //循環完成一趟排序,即將數組中小於temp的元素放在左邊,大於temp的元素放在右邊
 { while(j>i && R[j]>temp)    //從右往左掃描,找到一個小於temp的元素
                j--; if(i<j) { R[i]=R[j];             //放在temp的左邊
                ++i;                   //i右移一位
 } while(i<j && R[i]<temp)    //從左往右掃描,找到一個大於temp的元素
                ++i; if(<j) { R[j]=R[i];             //放在temp的右邊
                --j;                   //j右移一位
 } } R[i]temp; //將temp放在最終位置
        QuickSort(R,low,i-1);          //遞歸的對temp左邊的元素進行排序
        QuickSort(R,i+1,high);         //遞歸的對temp右邊的元素進行排序
 } }

 

 6、簡單選擇排序 blog

//簡單選擇排序 //算法思想:從頭至尾順序掃描序列,找出一個最小的元素和第一個元素進行交換,接着從剩下的元素中繼續這種選擇和交換,最終使序列有序。 //(1)時間複雜度分析: // ①:O(n²) //(2)空間複雜度分析: // ①:O(1):輔助存儲空間不隨待排序列規模的變化而變化,是個常量。
 viod SelectSort(int R[],int n) { int i,j,k; int temp; for(i=0;i<n;++i) { k=i; for(j=i+1;j<n;++j)  //該循環從無序序列中挑選出一個最小的元素
            if(R[k]>R[j]) k=j; /* 下面3句完成最小元素與無序序列第一個元素的交換 */ temp=R[i]; R[i]=R[k]; R[k]=temp; } }

 

 7、堆排序 排序

//堆排序 //算法思想: //(1)時間複雜度分析: // ①:O(nlog2₂n) //(2)空間複雜度分析: // ①:O(1):輔助存儲空間不隨待排序列規模的變化而變化,是個常量。

/* 本函數完成在數組R[low]到R[high]範圍內,對在位置low上的結點進行調整 */
void Sift(int R[],int low,int high)  //R[]中是一棵徹底二叉樹,因此元素的存儲必須從1開始
{ int i=low,j=2*i;                 //R[j]是R[i]的左孩子結點
    int temp=R[i]; while(j<=high) { if(j<high && R[j]<R[j+1])   //若右孩子較大,則把j指向右孩子
            ++j;                    //j變爲2*i+1
        if(temp<R[j]) { R[i]=R[j];              //將R[j]調整到雙親結點的位置上
            i=j;                    //修改i和j的值,以便繼續向下調整
            j=2*i; } else
            break;                  //調整結束
 } R[i]=temp;                      //被調整結點的中放入最終位置
} /* 堆排序函數 */ viod heapSort(int R[],int n) { int i; int temp; for(i=n/2;i>=1;--i)            //創建初始堆
 Sift(R,i,n); for(i=n;i>=2;--i)              //進行n-1次循環,完成堆排序
 { /* 如下3句換出了根結點中的元素,將其放入最終位置 */ temp=R[1]; R[1]=R[i]; R[i]=temp; Sift(R,1,i-1);             //在減小了1個元素的無序序列中進行調整
 } }

 

 8、歸併排序 遞歸

//歸併排序 //算法思想: //(1)時間複雜度分析: // ①:O(nlog2₂n) //(2)空間複雜度分析: // ①:O(1):輔助存儲空間不隨待排序列規模的變化而變化,是個常量。

void merge(int arr[],int low,int mid,int high) { int i,j,k; int n1=mid -low+1; int n2=high-mid; int L[n1],R[n2]; for(i=0;i<n1;i++) L[i]=arr[low+i]; for(j=0;j<n2;j++) R[j]=arr[mid+1+j]; i=0; j=0; k=low; while(i<n1 && j<n2) { if(L[i]<=R[j]) arr[k]=L[i++]; else arr[k]=R[j++]; k++; } while(i<n1) arr[k++]=L[i++]; while(j<n2) arr[k++]=R[j++]; } void mergeSort(int arr[],int low,int mid,int high) { if(low<high) { int mid=(low+high)/2; mergeSort(arr,low,mid); //歸併排序前半段
        mergeSort(a,mid+1,high);   //歸併排序後半段
        merge(A,low,mid,high);     //merge()函數:把A數組中low到mid和mid+1到high範圍內的兩段有序序列歸併成一段有序序列
 } }

 

          總結對比                 

快速排序、歸併排序 、堆排序三種排序算法的時間複雜度都是O(nlogn)
通常狀況下,就運行時間而言:快速排序 < 歸併排序 < 堆排序

三種排序算法的缺點:
  ①快速排序:極端狀況下排序效率低
  ②歸併排序:須要額外的內存開銷
  ③堆排序:在快的排序算法中相對較慢

相關文章
相關標籤/搜索