算法导论学习笔记-第二章-算法入门

第二章-算法入门

 

总结:这一章讲了插入排序及其算法分析,循环不变式的证明,合并排序(分治法)及其算法分析。

 

1. 插入排序

类似于扑克牌的插入过程,设A[1...j-1]是排好序的一个数组,将A[j]插入A[1...j-1]中使A[j]称为排好序的一个数组,j <- 2 to length[A]

 

算法分析:最好情况,整个数组已经是顺序的了,O(n)

               最坏情况,整个数组是逆序的,O(n^2)

               平均情况,O(n^2)

               是一种稳定排序

 

算法优化:将A[j]插入已经排好序的A[1...j-1]时,可以用二分查找法,这可以改变查找合适位置的效率,但是仍需要将数组的元素一个一个移动。

 

伪代码

INSERTION-SORT(A) for j<-2 to length[A] do key <- A[j] i <- j-1 while i>0 and A[i]>key do A[i+1] <- A[i] i <- i-1 A[i+1] <- key

 

C++代码

#include <iostream> using namespace std; void insertSort(int a[],int length) //°´´ÓСµ½´óÅÅÐò { for (int j=1;j<length;j++) { int key=a[j]; int i=j-1; while(i >= 0 && a[i] > key) { a[i+1]=a[i]; i--; } a[i+1]=key; } } int main() { int a[7]={3,4,2,1,8,0,9}; int length=sizeof(a)/sizeof(int); insertSort(a,length); for (int i=0;i<length;i++) { cout << a[i] << " "; } cout << endl; return 0; }

 

2. 循环不变式

证明三个性质:初始化、保持、终止

 

3. 合并排序

 

分治法:将原问题分成n个规模较小而结构与原问题相似的子问题;递归地解决这些子问题,然后再合并其结果,就得到原问题的解。

 

算法分析:时间复杂度O(nlgn)

               空间复杂度O(n)

               稳定排序

 

C++代码

void merge(int a[],int p,int q, int r) //合并已经排序的a[p...q-1]和a[q...r-1],从小到大排序 { int n1=q-p; int n2=r-q; int *left=new int[n1]; int *right=new int[n2]; int i; for(i=0;i<n1;i++) left[i]=a[p+i]; for(i=0;i<n2;i++) right[i]=a[q+i]; i=0; int j=0; int k=p; while(i<n1 && j<n2) { if(left[i]<right[j]) a[k++]=left[i++]; else a[k++]=right[j++]; } if(j<n2) { while(j<n2) a[k++]=right[j++]; } if(i<n1) { while(i<n1) a[k++]=left[i++]; } } void mergeSort(int a[],int begin,int end) //从a[begin]到a[end-1]合并排序 { if(begin>=end-1) return; int r=(begin+end)/2; mergeSort(a,begin,r); mergeSort(a,r,end); merge(a,begin,r,end); } int main() { int a[7]={3,4,2,1,8,0,9}; int length=sizeof(a)/sizeof(int); // insertSort(a,length); mergeSort(a,0,length); for (int i=0;i<length;i++) { cout << a[i] << " "; } cout << endl; while(1); return 0; }

 

 

【练习题】

1. 选择排序

 

从A中找到最小的元素,与A[0]互换,再从A中找出次最小元素,与A[1]互换,以此类推..

 

算法分析:时间复杂度 O(n^2) 

               稳定排序

 

伪代码:

 

SELECTION-SORT(A) for i <- 1 to length[A]-1 do smallest <- i for j <- i+1 to length[A] do if A[j]<A[smallest] then smallest <- j exchange A[i] <-> A[smallest]

问题:为什么i的循环只要到length[A]-1,因为到时前n-1个一定是最小的n-1个,那么第n个必定是最大的那个元素,所以最后一次循环可以省去了。

 

C++代码:

 

 void selectionSort(int a[],int length) { for(int i=0;i<length-1;i++) { int smallest=i; for(int j=i+1;j<length;j++) if(a[smallest]>a[j]) smallest=j; int temp=a[i]; a[i]=a[smallest]; a[smallest]=temp; } } int main() { int a[7]={3,4,2,1,8,0,9}; int length=sizeof(a)/sizeof(int); selectionSort(a,length); for (int i=0;i<length;i++) { cout << a[i] << " "; } cout << endl; while(1); return 0; }

 

2. 二分查找(时间复杂度O(lgn))

1) 非递归(迭代)

//二分查找(迭代) int iterativeBinarySearch(int a[],int v, int low, int high) { while(low <= high) { int mid=(low+high)/2; if(a[mid]==v) return mid; else if (v>a[mid]) low=mid+1; else high=mid-1; } return NULL; }

 

2) 递归

 int recursiveBinarySearch(int a[],int v, int low, int high) { if(low > high) return -1; int mid=(low+high)/2; if(v==a[mid]) return mid; else if(v>a[mid]) return recursiveBinarySearch(a,v,mid+1,high); else return recursiveBinarySearch(a,v,low, mid-1); }

 

 

3. 请给出一个运行时间为O(nlgn)的算法,使之能在给定一个由n个整数构成的集合S和另一个整数x时,判断出S中是否存在两个其和等于x的元素。

算法思路:

思路一:对S排序(O(nlgn)),去掉S中重复的元素(O(n)),S中的每个元素y,计算z=x-y,将所有的z汇集成S’(O(n)),对S’排序(O(nlgn)),合并SS’。若合并的集合中存在两个连续相等的元素,则说明S中存在两个其和等于x的元素。

void removeTheSame(int a[],int &length) { for(int i=1;i<length;i++) { if(a[i]==a[i-1]) { int j=i; while(j<length) { a[j]=a[j+1]; j++; } length--; } } } bool findSumX1(int a[],int x,int length) { mergeSort(a,0,length); removeTheSame(a,length); int *b=new int[length]; for(int i=0;i<length;i++) b[i]=x-a[i]; mergeSort(b,0,length); int len=length+length; int *c=new int[len]; for(int i=0;i< length;i++) c[i]=a[i]; for(int i=length;i<len;i++) c[i]=b[i-length]; merge(c,0,length,len); for(int i=1;i<len;i++) { if(c[i]==c[i-1]) return true; } return false; }

 

思路二:对S排序(O(nlgn)),对S中的元素y,计算z=x-y,从S中二分查找zO(lgn)),找到则说明存在。最多循环n次,复杂度O(nlgn)

bool findSumX2(int a[],int x,int length) { mergeSort(a,0,length); removeTheSame(a,length); for(int i=0;i<length;i++) { int z=x-a[i]; if(recursiveBinarySearch(a,z,0,length)!=-1) return true; } return false; }

 

 【思考题】

1. 用O(nlgn)的复杂度求给定n个元素的任何排列中逆序对的数目(提示:修改合并排序)

int merge2(int a[],int p,int q,int r) //a[p...q-1]和a[q...r-1] { int n1=q-p; int n2=r-q; int *left=new int[n1]; int *right=new int[n2]; int i; for(i=0;i<n1;i++) left[i]=a[p+i]; for(i=0;i<n2;i++) right[i]=a[q+i]; i=0; int j=0,k=p; int cnt=0; while(i<n1 && j<n2) { if(right[j]<left[i]) { a[k++]=right[j++]; cnt += n1-i; } else a[k++]=left[i++]; } if(i<n1) while(i<n1) a[k++]=left[i++]; if(j<n2) while(j<n2) a[k++]=right[j++]; return cnt; } int findInversion(int a[],int low, int high) { if(low>=high-1) return 0; int r=(low+high)/2; int count=0; count+=findInversion(a,low,r); count+=findInversion(a,r,high); count+=merge2(a,low,r,high); return count; }

你可能感兴趣的:(c,优化,算法,null,merge,n2)