【编程珠玑】第十一章 排序 (插入排序和快速排序的深度优化)

一,概述

1)插入排序

要找到合适的位置,需要判断前一个元素比t小而后一个元素比t大。然后将t插入正确位置。

比较a[j-1] 跟 a[j] 的关系很关键


isort1: 没有到达最终位置,就交换该元素和它前面的元素

#include

for(int i=1;i<5;i++) for(int j=i;j>0&&a[j-1]>a[j];j--) swap(a[j-1],a[j]);
  isort2:将库函数替换
int temp; for(int i=1;i<5;i++) for(int j=i; j>0 && a[j-1]>a[j]; j--) { temp=a[j]; a[j]=a[j-1]; a[j-1]=temp; }


isort3:减少移动次数


 for(i=1;i<5;i++) { t=a[i]; for(j=i; j>0 && a[j-1]>t; j--) a[j]=a[j-1]; a[j]=t; }
 2)快速排序
  qsort1:O(nlog(n)时间和O(logn)的栈空间
 #include"stdio.h" void Partition(int a[],int left,int right) { int i=left,j=right; int temp=a[i];//记录下轴的位置 if(left>=right)//退出条件 return; while(i=a[i])//从左侧查找,找到第一个比轴大的 i++; a[j]=a[i]; // 大的后移 } a[i]=temp;//找到轴的正确位置 Partition(a,left,j-1); Partition(a,j+1,right); } int main() { int a[10]={3,4,2,1,6,5,7,8}; Partition(a,0,7); for(int i=0;i<8;i++) printf("%d\n",a[i]); return 0; }


qsort2:双向划分避免所有元素都相等时候的最坏情况


#include #include using namespace std; void quictSort(int a[],int l,int u) { if(l >= u) return; int t = a[l];//记录轴 int i = l; int j = u+1; for(;;) { do i++;while(i <= u && a[i] < t);//先i++再判断条件 do j--;while(a[j] > t); //先j--再判断条件 if(i > j) //无限循环退出的条件 break; swap(a[i],a[j]);//每找到一个前面元素大于等于轴,跟后面小于等于轴的就交换 } swap(a[l],a[j]); quictSort(a,l,j-1); quictSort(a,j+1,u); } int main() { int a[10]={3,4,2,1,6,5,7,8}; quictSort(a,0,7); for(int i=0;i<8;i++) printf("%d\n",a[i]); return 0; }


思路:n个相同元素组成的数组,插入排序性能最好(每个元素需要移动的距离都为0),时间复杂度为o(n)。 快速排序的性能则很糟糕,n-1次划分每次划分都需要0(n)的时间来去掉一个元素,所以时间复杂度为O(n*n) 采取的对策是:采取两个内循环,第一个向右移过小元素,遇到大元素停止,第二个左移,遇到小元素停止。然后交换。 当遇到相同的元素时,停止扫描,交换a[i] 和 a[j]的值。最坏时间复杂度O(nlogn)


 qsort3:利用随机位置的数做轴,会使数组的快速排序得到优化。
#include #include using namespace std; void quictSort(int a[],int l,int u) { if(l >= u) return; swap(a[l],a[rand()%(u-l) + l]);//仅仅是在这里做文章 int t = a[l]; int i = l; int j = u+1; for(;;) { do i++;while(i <= u && a[i] < t); do j--;while(a[j] > t); if(i > j) break; swap(a[i],a[j]); } swap(a[l],a[j]); quictSort(a,l,j-1); quictSort(a,j+1,u); } int main() { int a[10]={3,4,2,1,6,5,7,8}; quictSort(a,0,7); for(int i=0;i<8;i++) printf("%d\n",a[i]); return 0; }


【知识点】产生某个范围随机数方法:
  比如要产生 [60-99]的随机数.
  使用rand()函数产生的随机数是从0(包括0)开始的
  我们思考:

  [ 0,?] + 60 =[60,99]
  很明显,?应该是39.产生[0, 39]的随机数可以这样做:rand() % 40 得解.


#include using namespace std; #include #include int main() { srand(time(NULL));//种子不同生产的随机数不同 for (int i=0; i!=50; ++i) { cout << rand() % 40 + 60 << endl; } }

rand()%(end-start)+start;


注意:因为rand()函数是按指定的顺序来产生整数,因此每次执行上面的语句都打印相同的值,所以说C语言的随机并不是真正意义上的随机,有时候也叫伪随机数


二,习题
     2)Lomuto的快速排序算法如下
#include #include using namespace std; void quictSort(int a[],int l,int u) { if(l >= u) return; int m = l;//记录轴 for(int i=l+1;i<=u;++i) { if(a[i] < a[l]) //小于轴 swap(a[++m],a[i]);//换到前面来 ++m说明轴的位置向后移动 } swap(a[l],a[m]); //找到轴的位置,交换 quictSort(a,l,m-1); quictSort(a,m+1,u); } int main() { int a[10]={3,4,2,1,6,5,7,8}; quictSort(a,0,7); for(int i=0;i<8;i++) printf("%d\n",a[i]); return 0; }
       
      采用哨兵优化后算法:(减少了内循环测试)
#include 
#include 
using namespace std;
void quictSort(int a[],int l,int u)
{
	if(l >= u)
		return;
	int m = u+1;//记录轴
    int i=  u+1;
	do{
		while(a[--i]
      
        5)没完全理解题意
        
        6)选择排序
#include #include using namespace std; void selectSort(int a[],int n) { for(int i=0;ia[j]) swap(a[i],a[j]); } } } int main() { int a[10]={3,4,2,1,6,5,7,8}; selectSort(a,7); for(int i=0;i<8;i++) printf("%d\n",a[i]); return 0; }
          希尔排序:选择排序的优化
#include"stdio.h" void shellsort(int a[],int n) { int k=n/2;//第一个间距 分成两个一组 while(k>0) { for(int i=k;i=0&&t

        9)线性时间寻找第k小元素
          思路:快速排序选择一个pivot对数组进行划分,左边小于pivot,右边大于等于pivot,所以我们计算左边小于pivot(加上pivot)的个数count总共有多少,如果等于k,正是我们所要的,如果大于k,说明第k小的数在左边,那就在左边进行我们的递归;否则,在右边,那么说明右边的第k-count小的数就是我们所要的,在右边进行我们的递归。
          算法实现一:
#include using namespace std; inline void Swap(int a[],int i,int j)//内联函数,交换两个元素位置 { int temp=a[i]; a[i]=a[j]; a[j]=temp; } int Partition(int a[],int p,int r)//根据pivot a[r]来划分数组 { int pivot=a[r]; int low=p-1; int i; for(i=p;i

算法实现二:

#include"stdio.h" int GetMinK(int A[],int n,int k) { int s=-1,i=0,j=n-1,temp; int beg=i; int end=j; while(s!=k) { beg=i; end=j; temp=A[i]; while(i=temp)j--;A[i]=A[j]; while(ik){i=beg;j--;} //在左侧寻找 if(s
14)算法实现如下:

#include #include using namespace std; void quictSort(int a[],int u) { if(u<=1) return; int m = 0;//记录轴 for(int i=1;i<=u;++i) { if(a[i] < a[0]) //小于轴 swap(a[++m],a[i]);//换到前面来 ++m说明轴的位置向后移动 } swap(a[0],a[m]); //找到轴的位置,交换 quictSort(a,m-1); quictSort(a+m+1,u-m-1); } int main() { int a[10]={3,4,2,1,6,5,7,8}; quictSort(a,7); for(int i=0;i<8;i++) printf("%d\n",a[i]); return 0; }









你可能感兴趣的:(【编程珠玑】第十一章 排序 (插入排序和快速排序的深度优化))