【數據結構】排序

本篇簡單梳理了下常見的七種排序算法:冒泡排序、簡單選擇排序、直接插入排序、希爾排序、堆排序、歸併排序、快速排序,以圖例形式,解釋如何進行排序。

該圖爲幾種排序算法的複雜度、穩定性等。

1. 冒泡排序
  • 思路:將鍵值較大的記錄向序列的尾部移動,鍵值較小的記錄向序列的前部移動。
    在這裏插入圖片描述
void BubbleSort(int* a, int n) //冒泡排序
{
        int end = n - 1;
        while (end > 0)
        {
               for (int i = 0; i < end; ++i)
               {
                       if (a[i] > a[i + 1])
                              Swap(&a[i], &a[i + 1]);
               }
               --end;
        }
}
2. 簡單選擇排序
  • 思路:每一次從待排序的數據元素中選出最小(或最大)的一個元素,存放在序列的起始位置,直到全部待排序的數據元素排完。
    在這裏插入圖片描述
void SelectSort(int* a, int n) //選擇排序
{
        int begin = 0;
        int end = n - 1;
        int max_index, min_index; //index->指針
        while (begin <= end)
        {
               min_index = max_index = begin;
               for (int i = begin + 1; i < end; ++i)
               {
                       if (a[i] > a[max_index])
                              max_index = i; //將大的元素地址給與max,方可經行下一輪比較
                       if (a[i] < a[min_index])
                              min_index = i;
                       Swap(&a[i], &a[max_index]); //&
                       if (max_index == begin)  //當最大值在開始時,需要將最小地址交換給最大地址,否則最大值的位置會有衝突
                       {
                              max_index = min_index;
                       }
                       Swap(&a[i], &a[min_index]); //&
               }
               --end;
               ++begin;
        }
}
3. 直接插入排序
  • 思路:當插入第i(i>=1)個元素時,前面的array[0],array[1],…,array[i-1]已經排好序,此時用array[i]的排序碼與array[i-1],array[i-2],…的排序碼順序進行比較,找到插入位置即將array[i]插入,原來位置上的元素順序後移。
    在這裏插入圖片描述
void InsertSort(int* a, int n) //直接插入排序
{
        //思路:在已排好的序列中插入數據,大於的數往後移動覆蓋,插入的數要大於前一個或者小於後一個(插入a[0]不用比較前一個)
        //插入的數據的位置爲a[0]-a[n-1],最多比較到n-2的位置
        //時間 O(n*n):最好O(n)、最壞O(n*n), 空間 O(1)、穩定
        for (int i = 0; i < n - 1; ++i)
        {
               int end = i; 
               int tmp = a[end + 1]; //要插入的數
               while (end >= 0) //=0 往原序列的頭插入
               {
                       if (a[end] > tmp)  //小往前插
                       {
                              a[end + 1] = a[end]; //覆蓋數
                              --end;
                       }
                       else
                       {
                              break;
                       }
               }
               a[end + 1] = tmp;  //前面已排好序,直接插入最後一個w位置
        }
}
4. 希爾排序
  • 思路:設置一個gap,gap的數值代表數據之間間隔gap,然後預排序,預排序完成後基本接近於排序,再進行一次插入排序。
    在這裏插入圖片描述
void ShellSort(int* a, int n)  //哈希排序
{
        int gap = n;
        while (gap > 1)
        {
               gap = gap / 3 + 1; //設置間距,+1確保進行插入排序 gap = 1 表示直接插入排序
               for (int i = 0; i < n - gap; ++i) //當i走到n-1-gap進行最後比較數據交換
               {
                       int end = i;
                       int tmp = a[end + gap];
                       while (end >= 0)
                       {
                              if (a[end] > tmp)
                              {
                                      a[end + gap] = a[end];
                                      end = end - gap;
                              }
                              else
                              {
                                      break;
                              }
                       }
                       a[end + gap] = tmp;
               }
        }
}
5. 堆排序
  • 排升序要建大堆,排降序建小堆。
    在這裏插入圖片描述
void AdujustDown(int* a, int n, int parent) //向下調整
{
        int child = parent * 2 + 1;
        while (child < n)
        {
               if (child + 1 < n && a[child] < a[child + 1]) //選擇小的孩子
                       ++child;
               if (a[child] > a[parent])
               {
                       Swap(&a[child], &a[parent]); //交換數據
                       parent = child;
                       child = parent * 2 + 1; //迭代
               }
               else
               {
                       break;
               }
        }
}

void HeapSort(int* a, int n) //堆排序
{
        for (int i = (n - 2) / 2; i >= 0; --i) //建堆
        {
               AdujustDown(a, n, i);
        }
        int end = n - 1; //堆排序
        while (end > 0)
        {
               Swap(&a[0], &a[end]); //*已建立大堆,將a[0]與a[end]交換,再經行堆調整
               AdujustDown(a, end, 0);
               --end;
        }
}
6. 歸併排序
  • 將已有序的子序列合併,得到完全有序的序列,即先使每個子序列有序,再使子序列段間有序;若將兩個有序表合併成一個有序表,稱爲二路歸併。
    在這裏插入圖片描述
void _MergeSort(int* a, int left, int right, int* tmp)
{
        //分解
        if (left == right) //子問題只左右兩個數
               return;
        int mid = left + (right - left) / 2;
        _MergeSort(a, left, mid, tmp);  //區間劃分[left, mid][mid+1, right]
        _MergeSort(a, mid + 1, right, tmp);

        //合併
        //左右之間有序,歸併
        int begin1 = left; int end1 = mid;
        int begin2 = mid + 1; int end2 = right;
        int  i = left; //tmp
        while (begin1 <= end1 && begin2 <= end2)
        {
               if (a[begin1] < a[begin2])
               {
                       tmp[i++] = a[begin1++];
               }
               else
               {
                       tmp[i++] = a[begin2++];
               }
        }
        while (begin1 <= end1)
        {
               tmp[i++] = a[begin1++];
        }
        while (begin2 <= end2)
        {
               tmp[i++] = a[begin2++];
        }
        //拷回原數組中,開始將數據均放於臨時數組中
        memcpy(a + left, tmp + left, sizeof(int)*(i - left)); //原、目標、大小(right-left+1)但針對每個小區間
}

void MergeSort(int* a, int n) //歸併排序
{
        int* tmp = (int*)malloc(sizeof(int)*n); //合併時,先把數放到tmp數組中,經排序後再覆蓋到原數組,直到最後一趟完成即排序完成
        _MergeSort(a, 0, n - 1, tmp);
        free(tmp);
}
7. 快速排序
  • 思路:任取待排序元素序列中的某元素作爲基準值,按照該排序碼將待排序集合分割成兩子序列,左子序列中所有元素均小於基準值,右子序列中所有元素均大於基準值,然後最左右子序列重複該過程,直到所有元素都排列在相應位置上爲止。
    在這裏插入圖片描述
int OneSort(int* a, int left, int right)
{
        int key = a[right];
        int key_index = right;
        
        while (left < right)
        {
               while (left < right && a[left] <= key) //a[left] <= key,進入循環++,大於時出來與右指針所找的數經行交換
                       ++left;
               while (left < right && a[right] >= key)
                       --right;
               Swap(&a[left], &a[right]);
               if (left < right)
               {
                       Swap(&a[left], &a[right]);
                       ++left;
                       --right;
               }
        }
        Swap(&a[left], &a[key_index]); //left = right時,選取右key,則a[left] 與 key所指的數值交換
        
        return left;
}
void QuickSort1(int* a, int left, int right) //快速排序
{
        //思路:左右指針法,在最左或最右選取一個key
        //左指針向左找大於key所對應的數,右指針找小於key所對應的數值,找到左右指針對應的數值交換
        //右邊作key,先走左,後走右;左邊爲key,先走右,後走左;指針相遇時,left與右key交換、right與左key交換
        if (left >= right)  //判斷左右區間大小
               return;

        int key_index = OneSort1(a, left, right); //區間分爲三部分:[left, key_index-1]  key [key_index + 1, right]
        QuickSort1(a, left, key_index-1);
        QuickSort1(a, key_index + 1, right);
}

【Yang】

  • 哪裏有問題,希望大家提出來,我會及時改正 o( ̄▽ ̄)ブ 。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章