第八章 排序
8_3
01、在使用非遞歸方法實(shí)現(xiàn)快速排序時(shí),通常要用一個(gè)棧來(lái)記憶待排序區(qū)間的兩個(gè)端點(diǎn)。能否用隊(duì)列 來(lái)實(shí)現(xiàn)這個(gè)棧,為什么?
答:能
棧的作用是在處理一個(gè)子區(qū)間時(shí),保存另一個(gè)子區(qū)間的上界和下界(排序過(guò)程可能產(chǎn)生新的左右子區(qū)間),待該區(qū)間處理完后,再?gòu)臈V腥〕隽硪粋€(gè)子區(qū)間的 邊界。用隊(duì)列也可以實(shí)現(xiàn),只是處理子區(qū)間的順序有所變動(dòng)。
(棧保存相當(dāng)于先處理最小的區(qū)間,再一層層返回,直至大區(qū)間;而隊(duì)列則是逐層逐個(gè)區(qū)間依次處理)
02、編寫(xiě)雙向冒泡排序算法,在正反兩個(gè)方向交替進(jìn)行分掃描,即第一趟把關(guān)鍵字最大的元素放在序列的最后面,第二趟把關(guān)鍵字最小的元素放在序列的最前面,如此反復(fù)進(jìn)行。
//雙向起泡//思路:// 第一趟把最大的元素放在序列的最后面 即從前往后掃描 成功后 修改上界// 第二趟把最小的元素放在序列的最前面 即從后往前掃描 成功后 修改下界void D_BubbleSort(ElemType a[],int n){int low=0,high=n-1;int i,j;bool flag=true;//記錄每趟是否交換的標(biāo)志while(low<high&&flag){flag=false;//將最大的元素放在最后面 for(i=low;i<high;i++)if(a[i]>a[i+1]){swap(a[i],a[i+1]);flag=true;//交換 則置flag為true }high--;//修改上界//將最小的元素放在最前面 for(i=high;i>low;i--)if(a[i-1]>a[i]){swap(a[i-1],a[i]);flag=true;} low++;//修改下界 } }03、已知線性表按順序存儲(chǔ),且每個(gè)元素都是不相同的的整數(shù)型元素,設(shè)計(jì)把所有奇數(shù)移動(dòng)到所有偶數(shù)前邊的算法(要求時(shí)間最少,輔助空間最少)
//雙指針 //low從前往后掃描 high從后往前掃描 //如果a[low]是奇數(shù)則low++ a[high]是偶數(shù)則high-- //若掃描過(guò)程中 出現(xiàn)a[low]是偶數(shù) a[high]是奇數(shù) 則交換void move(ElemType a[],int n) {int low=0,high=n-1;while(low<high){while(low<high&&a[low]%2==1) low++;//從前往后找到第一個(gè)偶數(shù) while(low<high&&a[high]%2==0) high--;//從后往前找到第一個(gè)奇數(shù)if(low<high){swap(a[low],a[high]);low++;high--;//修改指針 繼續(xù)掃描 }} }04、試重新編寫(xiě)考點(diǎn)精析中的快速排序的劃分算法,使之每次選取的樞軸值都是隨機(jī)地從當(dāng)前子表中選擇的。
//只需在原來(lái)代碼前加兩行 int rand_pos=low+rand%(high-low+1); swap(a[rand_pos],a[low]);05、試編寫(xiě)一個(gè)算法,使之能夠在數(shù)組L(1..n)中找出第k小的元素(即從小到大排序后處于第k個(gè)位置的元素)。
#include <iostream>using namespace std;typedef int ElemType;const int N=10010; int L[N],n,k;//找出L[1 2 3... n]中第k小的元素//直接想法:直接對(duì)L[]中元素排序 返回L[k] //2--王道 //思路:蘊(yùn)含快排+分治 //選取一個(gè)樞軸元素pivot 使用partition找到樞軸元素在順序表中的位置pos //如果 pos==k 則返回pivot //如果 pos>k 則說(shuō)明要找的元素在pos左邊 即L[1...pos]里 于是在這段區(qū)間找第k個(gè)位置的元素 //如果 pos<k 則說(shuō)明要找的元素在pos右邊 即L[pos... n]里 于是在這段區(qū)間找第k-pos個(gè)位置的元素即可 ElemType search_k_th(ElemType L[],int low,int high,int k) {int pivot=L[low];int low_pos=low;int high_pos=high;//由于下面會(huì)修改low 與 high 故先保存 以便遞歸時(shí)使用 while(low<high){while(low<high&&L[high]>=pivot) high--;//找到比pivot小的元素 放在左邊L[low]=L[high];while(low<high&&L[low]<=pivot) low++;//找到比pivot大的元素 放在右邊L[high]=L[low]; }L[low]=pivot;//將pivot放在最終位置上if(low==k) //low與k相等 直接返回 return L[low];else if(low>k)//在前一部分找第k個(gè)元素 return search_k_th(L,low_pos,low-1,k);elsereturn search_k_th(L,low+1,high_pos,k);//因?yàn)榉祷氐氖悄硞€(gè)位置的元素 所以這里參數(shù)還是傳入k 并未k-low } int main() {cout<<"輸入元素個(gè)數(shù):";cin>>n;for(int i=1;i<=n;i++) cin>>L[i];cout<<"初始元素序列為:";for(int i=1;i<=n;i++) cout<<L[i]<<" ";cout<<endl;cout<<"要找的元素序號(hào):";cin>>k;int res=search_k_th(L,1,n,k);cout<<"第"<<k<<"小的元素為:"<<res<<endl; return 0; }//該算法 時(shí)間復(fù)雜度為O(n)06、荷蘭國(guó)旗問(wèn)題:設(shè)有一個(gè)僅由紅、白、藍(lán)三種顏色的條塊組成的條塊序列,請(qǐng)編寫(xiě)一個(gè)時(shí)間復(fù)雜度為O(n)的算法,使得這些條塊按紅、白、藍(lán)的順序排好,即排成荷蘭國(guó)旗圖案。
#include <stdio.h> #include <string.h> #include <stdlib.h>//荷蘭國(guó)旗問(wèn)題 //思路:設(shè)置三個(gè)指針i,j,k;起初i指向線性表第一個(gè)元素 k指向線性表最后一個(gè)元素 j為工作指針 //j從頭到尾掃描一遍線性表 //如果j為紅色 則與i指針?biāo)割伾粨Q(交換到前面);j為藍(lán)色 則與k指針?biāo)割伾粨Q(交換到后面);j為白色則j++typedef enum{red,white,blue }color;color a[100];void swap(color &m,color &n) {color temp=m;m=n;n=temp;} void flag_Arrange(color a[],int n) {int i=0,j=0,k=n-1;while(j<=k){switch(a[j]){case red:swap(a[i],a[j]);i++,j++;break;case white:j++;break;case blue:swap(a[j],a[k]);k--;//這里不能j++ 因?yàn)閍[i] 和 a[j]交換后仍可能為blue break;}} }int main() {int n;//元素個(gè)數(shù)char input[20]={0};scanf("%d",&n);for(int i=0;i<n;i++){scanf("%s",input);if(strcmp(input,"red")==0){a[i]=red;}else if(strcmp(input,"white")==0){a[i]=white;}else if(strcmp(input,"blue")==0){a[i]=blue;}memset(input,0,sizeof(input));}flag_Arrange(a,n);for(int i=0;i<n;i++){switch(a[i]){case red:printf("red ");break;case white:printf("white ");break;case blue:printf("blue ");break; }}}07、
#include <iostream> using namespace std;const int N=10010;int A[N],A1[N],A2[N];//A1存放A中更大的元素 A2存放A中更小的元素 //思路: //要使|n1-n2|最小 同時(shí)|S1-S2|最大 則將排序后的前一半元素放入A2(其中元素更小) 后一半元素放入A1(其中元素更大) //當(dāng)n是偶數(shù)時(shí)剛好一邊一半 當(dāng)n是奇數(shù)時(shí)則前n/2個(gè)元素放入A2(向下取整) 剩余的所有元素放入A1void Arrange(int A[],int n,int low,int high,int A1[],int A2[]) {//n代表元素個(gè)數(shù) low初始為0 high為n-1int pos=n/2;int flag=1;//標(biāo)志 若完成了劃分 則置flag為0 int low0=0,high0=n-1;//初始時(shí) 考察范圍為[0,n-1] //用快排的思想找位置 while(flag){int pivot=A[low];while(low<high){while(low<high&&A[high]>=pivot) high--;//從左邊找比pivot小的元素 找到放到右邊A[low]=A[high];while(low<high&&A[low]<=pivot) low++;//從右邊找比pivot大的元素 找到放在左邊A[high]=A[low]; }A[low]=pivot;//出while后 pivot的位置就定了if(low==pos-1)//如果low的位置是 目標(biāo)位置 {flag=0;//則置flag為0 結(jié)束循環(huán) }else if(low<pos-1)//若low比目標(biāo)位置更小 則只需考察low之后的元素 {//此時(shí) 快排劃分的范圍變成了[low+1,high0] low0=++low;//修改指針low 為low+1 同時(shí)保存low0為下一次劃分做準(zhǔn)備 因?yàn)橄乱淮问歉〉姆秶?high=high0;//修改指針high 指向high0 }else//若low比目標(biāo)位置更大 則只需考慮low之前的元素 {//此時(shí) 快排劃分的范圍變成[low0,high-1] high0=--high;//修改指針high為high-1 同時(shí)改變high0為下一次劃分做準(zhǔn)備 下一次一定是更小的范圍 low=low0;//修改指針low 指向low0 } }for(int i=0;i<pos;i++) A2[i]=A[i];for(int k=0,i=pos;i<n;i++) A1[k++]=A[i]; } int main() {int n;cout<<"待劃分元素個(gè)數(shù):";cin>>n;for(int i=0;i<n;i++) cin>>A[i];cout<<"A中元素為:";for(int i=0;i<n;i++) cout<<A[i]<<" ";cout<<endl;Arrange(A,n,0,n-1,A1,A2);cout<<"A1表中元素為:";if(n&1){for(int i=0;i<=n/2;i++) cout<<A1[i]<<" ";}else{for(int i=0;i<n/2;i++) cout<<A1[i]<<" ";}cout<<endl;cout<<"A2表中元素為:";for(int i=0;i<n/2;i++) cout<<A2[i]<<" ";return 0; }8_4
04、編寫(xiě)一個(gè)算法,在基于單鏈表表示的待排序關(guān)鍵字序列上進(jìn)行簡(jiǎn)單選擇排序。
//基于單鏈表的簡(jiǎn)單選擇排序 //1---無(wú)需斷鏈 每次找到后交換data值 void select_LinkSort(LinkList L) {LNode *p=L->next;//指向首元素結(jié)點(diǎn)LNode *min,*q; int temp;//保存臨時(shí)值 while(p){min=p;q=p->next;while(q)//尋找最小元素結(jié)點(diǎn) {if(q->data<min->data)min=q;q=q->next;}//找到最小元素結(jié)點(diǎn)后 交換兩個(gè)結(jié)點(diǎn)的數(shù)據(jù)域 if(min!=p){temp=min->data;min->data=p->data;p->data=temp;;} p=p->next;}} //2---斷鏈 每次找到單鏈表的最大結(jié)點(diǎn) 然后將其頭插入鏈表中LinkList select_LinkSort_2(LinkList L) {LNode *h=L->next,*pre,*p,*maxpre,*max;L->next=NULL;//將L從頭結(jié)點(diǎn)斷開(kāi) 方便頭插//直到h掃描到鏈表尾 即NULL while(h){pre=maxpre=NULL,p=max=h;//找到最大元素結(jié)點(diǎn) while(p){if(p->data>max->data){max=p;maxpre=pre;}pre=p;p=p->next;}if(max==h)//最大結(jié)點(diǎn) 為首元素結(jié)點(diǎn) h=h->next;//直接讓h后移else//否則 將該元素結(jié)點(diǎn)摘除 保證鏈表連接 maxpre->next=max->next; //頭插max->next=L->next;L->next=max; } return L;}05、試設(shè)計(jì)一個(gè)算法,判斷一個(gè)數(shù)據(jù)序列是否構(gòu)成一個(gè)小根堆。
//判斷一個(gè)數(shù)據(jù)序列是否構(gòu)成小根堆(用順序表存儲(chǔ)堆 且視為完全二叉樹(shù))//思路:與建堆類(lèi)似 判斷l(xiāng)en/2的左右孩子(如果有的話(huà)) 然后依次上升到第一個(gè)位置 bool Judge_minHeap(ElemType a[],int len) {if(len%2==0)//結(jié)點(diǎn)個(gè)數(shù)為偶數(shù) 有一個(gè)單分支結(jié)點(diǎn) {if(a[len/2]>a[len])return false;for(int i=len/2-1;i>=1;i--){if(a[i]>a[2*i]||a[i]>a[2*i+1])//若父母結(jié)點(diǎn)大于左孩子或者右孩子結(jié)點(diǎn) return false;//則不是小根堆 }}else//結(jié)點(diǎn)個(gè)數(shù)為奇數(shù) 則每個(gè)分支節(jié)點(diǎn)都有左右孩子 {for(int i=len/2;i>=1;i--){if(a[i]>a[2*i]||a[i]>a[2*i+1])return false;}}return true;}8_6
02、
//1--插入排序void Insert_Sort(ElemType a[],int m,int n) {int i,j;for(i=m+1;i<=m+n;i++){a[0]=a[i];for(j=i-1;a[j]<a[0];j--)a[j+1]=a[j];a[j+1]=a[0];}} //最壞情況下 元素比較次數(shù)O(mn) 移動(dòng)次數(shù)O(mn) 時(shí)間復(fù)雜度O(mn) //空間復(fù)雜度 O(1)//2--歸并排序 ElemType *b=(ElemType *)malloc((m+n+1)*sizeof(ElemType)); void MergeSort(ElemType a[],int m,int n) {int i,j,k;for(int k=0;k<m+n;k++) b[k]=a[k];for(i=0,j=m,k=0;i<m&&j<n;k++){if(b[i]<=b[j]) a[k]=b[i++];elsea[k]=b[j++];} while(i<m) a[k++]=b[i++];while(j<n) a[k++]=b[j++];} //時(shí)間復(fù)雜度:O(m+n) 空間復(fù)雜度:O(m+n)03、
#include <stdio.h> #include <stdlib.h>typedef int ElemType;//計(jì)數(shù)排序void Count_Sort(ElemType a[],int n)//n表示有n個(gè)元素 {ElemType *b=(ElemType *)malloc((n+1)*sizeof(ElemType));int cnt=0;//記錄小于關(guān)鍵碼的元素個(gè)數(shù)int i,j;//工作指針 for(i=0;i<n;i++){int key=a[i];for(j=0;j<n;j++){if(a[j]<key)cnt++;}b[cnt]=key;cnt=0;//一輪循環(huán)后 cnt置0 } for(i=0;i<n;i++) a[i]=b[i];//拷貝回原數(shù)組 } int main() {int n;scanf("%d",&n);int a[n];printf("排序前:");getchar();for(int i=0;i<n;i++){scanf("%d",&a[i]);getchar();}printf("\n");Count_Sort(a,n);printf("排序后:");for(int i=0;i<n;i++) printf("%d ",a[i]);return 0; }//比較次數(shù) n2 //簡(jiǎn)單選擇排序比本排序號(hào) 簡(jiǎn)單選擇排序只比較 n*(n-1)/2//若限制任意兩個(gè)記錄只比較一次const int N=10010;int a[N],b[N],s[N];//s[N]作為計(jì)數(shù)器 void Count_Sort_2(ElemType a[],int n) {int i,j;for(i=0;i<n;i++){for(j=i+1;j<n;j++){if(a[i]<a[j])//若當(dāng)前元素比后序掃描的元素小s[a[j]]++; //則讓掃描到的元素的計(jì)數(shù)域+1 elses[a[i]]++;//否則 讓當(dāng)前元素的計(jì)數(shù)域+1}b[s[a[i]]]=a[i];}for(int i=0;i<n;i++) a[i]=b[i];04、設(shè)有一個(gè)數(shù)組中存放了一個(gè)無(wú)序的關(guān)鍵序列K1,K2,...Kn?,F(xiàn)要求將Kn放在將元素排序后的正確位置上,試編寫(xiě)實(shí)現(xiàn)該功能的算法,要求比較關(guān)鍵字的次數(shù)不超過(guò)n。
//以Kn為樞軸元素進(jìn)行一次快排 int Partition(ElemType a[],int high,int low) {low=0,high=n;int pivot=a[high];while(low<high){while(low<high&&a[low]<=pivot) low++;//從前往后找比pivot大的元素 找到則交換 a[high]=a[low];while(low<high&&a[high>=pivot]) high--;//從后往前找比pivot小的元素 找到則交換 a[low]=a[high];}a[low]=pivot;//將樞軸元素放在最終位置上 return low; }總結(jié)
- 上一篇: 蓝桥杯知识点(大纲)
- 下一篇: Javaer换坑指南之Linux