日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【算法学习笔记】堆排序和归并排序、其他几种排序的代码实现、比较和应用(习题)

發布時間:2024/9/30 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【算法学习笔记】堆排序和归并排序、其他几种排序的代码实现、比较和应用(习题) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 基本堆排序
  • 1.有20個數組,每個數組有500個元素,且是有序的,如何在20*500個數中找出排名前500的數
    • 設有兩個遞增的序列a,b 長度都為n,求前k個最小的a[i]+b[j]
    • 若要在N個海量數據(超過10億,不能一次性放入內存)中找出最大的k個元素,(內存中可以容納k個元素),最好采取什么數據結構和策略?
  • 歸并排序
    • 設有一個整數序列,求數組中一共多少個逆序對
  • 快速排序


基本堆排序

堆是具有以下性質的完全二叉樹:每個結點的值都大于或等于其左右孩子結點的值,稱為大頂堆;或者每個結點的值都小于或等于其左右孩子結點的值,稱為小頂堆。如下圖
調整為大根堆:
大頂堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]

小頂堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
將待排序序列構造成一個大頂堆,此時,整個序列的最大值就是堆頂的根節點。將其與末尾元素進行交換,此時末尾就為最大值。然后將剩余n-1個元素重新構造成一個堆,這樣會得到n個元素的次小值。如此反復執行,便能得到一個有序序列了

步驟一 構造初始堆。將給定無序序列構造成一個大頂堆(一般升序采用大頂堆,降序采用小頂堆)。

a.將無需序列構建成一個堆,根據升序降序需求選擇大頂堆或小頂堆;

b.將堆頂元素與末尾元素交換,將最大元素"沉"到數組末端;

c.重新調整結構,使其滿足堆定義,然后繼續交換堆頂元素與當前末尾元素,反復執行調整+交換步驟,直到整個序列有序。
https://www.cnblogs.com/chengxiao/p/6129630.html

void shif(int a[],int low,int high) {int i = low,j = 2*i;//j為左孩子int temp = a[i];while(j <= high){if(j < high && a[j] < a[j+1])//如果右孩子更大j++;if(temp < a[j]){//如果根節點小于最大孩子a[i] =a[j];//將a[j]調整到雙親結點位置上i = j;j = 2*i;//繼續向下調整}else break;//如果根節點大于等于最大的左右孩子}a[i] = temp; } void HeapSort(int a[],int n) {int i;for(i = n/2;i >= 1;i--){shif(a,i,n);//初始化循環建立堆}for(i = n;i >= 2; i--)//進行n-1趟堆排序,每一趟堆中元素個數減一{swap(a[i],a[1]);//將最后一個元素與a[1]交換shif(a,1,i-1);//對a[1..i-1]進行篩選,得到i-1個結點的堆} }int main() {int a[6]={0,11,74,3,15,6}; HeapSort(a,5); for(int i = 1;i <= 5;i++) cout<<a[i]<<" ";}
  • 每一趟通過堆調整產生有序區,它是全局有序區
  • 每次調整時間為o(log2n)
  • 產生初始堆的時間為o(n),比較次數約4n
  • 最好最壞和平均時間復雜度為o(nlog2n)
  • 是不穩定的排序算法

應用:STL中的priority_queue優先隊列
一旦元素在優先隊列容器中,刪除操作將刪除最高優先級元素

1.有20個數組,每個數組有500個元素,且是有序的,如何在20*500個數中找出排名前500的數

涉及知識點:

priority_queue中元素的比較 模板申明帶3個參數:priority_queue<Type, Container, Functional>Container必須是用數組實現的容器,比如vector,deque等等,但不能用 list。STL里面默認用的是vector。
比較方式默認用operator<,所以如果把后面2個參數缺省的話,優先隊列就是大頂堆(降序),隊頭元素最大
仿函數本質就是類重載了一個operator(),創建一個行為類似函數的對象。

template <class T> struct greater : public binary_function<T, T, bool> {bool operator()(const T& x, const T& y) const { return x > y; } };

題目思路:假設元素類型為int,有序數組的個數為max,利用堆結構,堆中結點類型為node,用vectormyv存儲結果。
下面是假如有三個有序數組,找出前三個最小的元素的代碼:

#include<stdio.h> #include<vector> #include<queue> #define max 3 struct node{//小根堆的結點類型int pn;//段號int sn;//該段號中元素的序號int data;//元素值 }; struct cmp{bool operator() (const node& s1, const node& s2)const{return s1.data > s2.data;//創建小根堆} };void Top(int *a[max],vector<int>&myv) {priority_queue<node, vector<node>, cmp>qu;//優先隊列,注意其中的三個參數 這樣每次出隊時都是元素值最小的結點struct node e;for(int i = 0;i < max; i++){e.pn = i;e.sn = 0;e.data = a[e.pn][e.sn];//將所有段的首元素入隊qu.push(e);}for(int i = 0;i < max; i++){ e = qu.top(); qu.pop(); myv.push_back(e.data);//元素值插入到vector中 e.sn++;//該段的下一個元素入隊 e.data = a[e.pn][e.sn];qu.push(e);} }void dis(vector<int>p) {vector<int>::iterator it;for(it = p.begin() ; it != p.end(); it++){cout<< *it<<" ";} }int main() { int a1[3] = {2,6,9}; int a2[3] = {-1,-1,5}; int a3[3] = {-3,2,4}; vector<int> my; int *a[max]; a[0] = a1; a[1] = a2; a[2] = a3; Top(a,my); dis(my);}

設有兩個遞增的序列a,b 長度都為n,求前k個最小的a[i]+b[j]

與上一題類似,利用小根堆,先將a[0]+b[0]插入到小根堆qu中,當k>0時循環,出隊元素e,插入到result中,k–,vector<node>result存放結果

void Top(int a[],int b[],int n,int k,vector<node>&p)//求前k個最小和 {priority_queue<node>qu;//優先隊列,注意跟上一題比較,這里只需要一個參數 這樣每次出隊時都是元素值最小的結點struct node e,temp; e.i = 0;e.j = 0;e.data = a[e.i]+b[e.j];qu.push(e);while(k){ e = qu.top(); qu.pop(); p.push_back(e); k--; if(e.j+1 < n){ temp.i = e.i; temp.j = e.j + 1; temp.data = a[temp.i++]+b[temp.j]; qu.push(temp); } if(e.j == 0 && e.i+1< n)//將a[e1.i+1]+b[e1.0]入隊 { temp.i = e.i+1; temp.j = e.j; temp.data = a[temp.i]+ b[temp.j]; qu.push(temp); }} }void dis(vector<node>p) {for(int i = 0;i < p.size(); i++){cout<< p[i].data<<" ";} }int main() { int a[3] = {2,4,5}; int b[3] = {1,2,6}; vector<node>vec; Top(a,b,3,6,vec);dis(vec);}

注:在b沒有掃描完時將a[e1.i]+b[e1.j+1]入隊,當e.j = 0時還要將a[ei.i+1]+b[e1.0]入隊

經過自己的試驗,去掉e.j == 0 && 這句話,輸出結果也是一樣的

若要在N個海量數據(超過10億,不能一次性放入內存)中找出最大的k個元素,(內存中可以容納k個元素),最好采取什么數據結構和策略?

首先讀入k個元素,假設第一次讀取的k個元素就是前k個最大元素,把k個元素建成小根堆,然后從第k+1個元素開始,讀取的每個元素d都與堆頂元素進行比較,如果元素d大于堆頂元素,則把堆頂的元素替換成d,再將其調整為小根堆,當所有數據都讀入并比較完之后,這個小根堆里面的所有元素就是最大的k個元素。

歸并排序

分為自底向上的非遞歸過程和自頂向下的遞歸過程,每一趟產生的是局部有序區, 其最好 最壞 平均時間復雜度均為o(nlog2n)
是不穩定的排序算法。
空間復雜度為o(n)
(快速排序的空間復雜度是o(log2n)

設有一個整數序列,求數組中一共多少個逆序對

可以采用歸并排序,在合并的過程中計算逆序數,這是一種高效的算法,時間復雜度遠小于暴力解法。
設low<=i<=mid,mid+1<=j<=high,當a[i]<=a[j]時不產生逆序對,當a[i]>a[j]時逆序數有mid-i+1個(注意兩半分別已經排好序了)

int sum; void merge(int a[], int low, int mid, int high) {int i = low;int j = mid + 1;int *tmp = (int *) malloc((high - low +1)* sizeof(int));//分配足夠空間int k = 0;while(j <= high && i <= mid){ if(a[i] > a[j]) {sum += mid - i + 1;tmp[k++] = a[j++]; } else tmp[k++] = a[i++];}while(j <= high)tmp[k++] = a[j++];while(i <= mid)tmp[k++] = a[i++];for(int k1 = 0;k1 < k;k1++)a[low+k1] = tmp[k1];//把tmp[0..k-1]復制到a[low..high]中free(tmp);//釋放空間 }void merge_sort(int a[],int low,int high) {if(low < high){//注意一開始的條件判斷int mid = (low + high)/2;merge_sort(a,low,mid);//先不斷遞歸 得到最底層的數字 再合并merge_sort(a, mid+1, high);merge(a,low,mid,high);} }int main() { int a[5] = {3,1,4,5,2}; merge_sort(a, 0, 4); cout<<sum;}

快速排序

快速排序之所以比較快,是因為相比冒泡排序,每次交換是跳躍式的。每次排序的時候設置一個基準點,將小于等于基準點的數全部放到基準點的左邊,將大于等于基準點的數全部放到基準點的右邊。這樣在每次交換的時候就不會像冒泡排序一樣只能在相鄰的數之間進行交換,交換的距離就大得多了。因此總的比較和交換次數就少了,速度自然就提高了。當然在最壞的情況下,仍可能是相鄰的兩個數進行了交換。因此快速排序的最差時間復雜度和冒泡排序是一樣的,都是O(N2),它的平均時間復雜度為O (NlogN)。其實快速排序是基于一種叫做“二分”的思想。

#include <stdio.h> int a[101],n;//定義全局變量,這兩個變量需要在子函數中使用 void quicksort(int left,int right) { int i,j,t,temp; if(left>right) return; temp=a[left]; //temp中存的就是基準數 i=left; j=right; while(i!=j) { //順序很重要,要先從右往左找 while(a[j]>=temp && i<j) j--; //再從左往右找 while(a[i]<=temp && i<j) i++; //交換兩個數在數組中的位置 if(i<j)//當哨兵i和哨兵j沒有相遇時 { t=a[i]; a[i]=a[j]; a[j]=t; } } //最終將基準數歸位 a[left]=a[i]; a[i]=temp; quicksort(left,i-1);//繼續處理左邊的,這里是一個遞歸的過程 quicksort(i+1,right);//繼續處理右邊的,這里是一個遞歸的過程 } int main() { int i,j,t; //讀入數據 scanf("%d",&n); for(i=1;i<=n;i++) scanf("%d",&a[i]); quicksort(1,n); //快速排序調用 //輸出排序后的結果 for(i=1;i<=n;i++) printf("%d ",a[i]); getchar();getchar(); return 0; }

總結

以上是生活随笔為你收集整理的【算法学习笔记】堆排序和归并排序、其他几种排序的代码实现、比较和应用(习题)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。