程序员面试宝典_第13章_数据结构基础_排序算法小结(1)

一、冒泡算法

[算法思想]:将被排序的记录数组R[0..n-1]垂直排列,每个记录R[i]看作是重量为 R[i].key的气泡。根据轻气泡不能在重气泡之下的原则,从下往上扫描数组R,凡扫描到违反本原则的轻气泡,就使其向上"飘浮"。如此反复进行,直到最后任何两个气泡都是轻者在上,重者在下为止。

#include<iostream>
using namespace std;
void BubbleSort(int R[],int n)//R(0..n-1)是待排序的文件,采用自下向上扫描,对R做冒泡排序
 {    

int i,j;
bool flag; //交换标志
	for(i=0;i<n-1;i++) //外循环控制循环趟数,最多做n-1趟排序
	{
		flag=false;
		int temp;
		 for(j=n-1;j>i;j--) //对当前无序区R[i..n-1]自下向上扫描

			 if(R[j]<R[j-1])
			 {
				 temp=R[j]; 
				 R[j]=R[j-1];
				 R[j-1]=temp;
				 flag=true;

			 }

		 if(!flag) return;//本趟排序未发生交换,提前终止算法

	} 

} 

int main()
{
	int a[10]={5,8,4,78,12,96,1,4,16,50};
	int i;
	for(i=0;i<10;i++)
	cout<<a[i]<<" ";
	cout<<endl; 
    BubbleSort(a,10);
	for(i=0;i<10;i++)
	cout<<a[i]<<" ";
	cout<<endl; 

system("pause");
return 0;
}


[算法思想]:将被排序的记录数组R[0..n-1]垂直排列,每个记录R[i]看作是重量为 R[i].key的气泡。根据重气泡不能在轻气泡之上的原则,从上往下扫描数组R,凡扫描到违反本原则的重气泡,就使其向下"下沉"。如此反复进行,直到最后任何两个气泡都是轻者在上,重者在下为止。

#include<iostream>
using namespace std;
void BubbleSort(int R[],int n)//R(0..n-1)是待排序的文件,采用自下向上扫描,对R做冒泡排序
 {    

int i,j;
bool flag; //交换标志
	for(i=0;i<n-1;i++) //外循环控制循环趟数,最多做n-1趟排序
	{
		flag=false;
		int temp;
		 for(j=0;j<n-i-1;j++) //对当前无序区R[i..n-1]自上向下扫描

			 if(R[j]>R[j+1])
			 {
				 temp=R[j]; 
				 R[j]=R[j+1];
				 R[j+1]=temp;
				 flag=true;

			 }

		 if(!flag) return;//本趟排序未发生交换,提前终止算法

	} 

} 

int main()
{
	int a[10]={5,8,4,78,12,96,1,4,16,50};
	int i;
	for(i=0;i<10;i++)
	cout<<a[i]<<" ";
	cout<<endl; 
    BubbleSort(a,10);
	for(i=0;i<10;i++)
	cout<<a[i]<<" ";
	cout<<endl; 

system("pause");
return 0;
}

注意:冒泡排序的空间复杂度为常量O(1),表示只需要一个辅助的temp存储单元来实现交换数据,即冒泡排序为就地排序。

冒泡排序的时间复杂度最好为O(n),最坏为O(n^2);
 

二、鸡尾酒算法(双向冒泡算法)

[算法思想]:数组中的数字本是无规律的排放,先找到最小的数字,把他放到第一位,然后找到最大的数字放到最后一位。然后再找到第二小的数字放到第二位,再找到第二大的数字放到倒数第二位。以此类推,直到完成排序

#include<iostream>
using namespace std;
void CocktailSort(int R[],int n)
{    

	int i,j,tail;
	tail=n-1;
	bool flag; //交换标志
	for(i=0;i<tail;) 
	{
		flag=false;
		int temp;
		for(j=tail;j>i;j--)//第一轮,先将最小的数据排到前面

			if(R[j]<R[j-1])
			{
				temp=R[j]; 
				R[j]=R[j-1];
				R[j-1]=temp;
				flag=true;

			}
			i++;//原来i处数据已排好序,加1

		for(j=i;j<tail;j++) //第二轮,将最大的数据排到后面

			if(R[j]>R[j+1])
			{
				temp=R[j]; 
				R[j]=R[j+1];
				R[j+1]=temp;
				flag=true;

			}
			tail--;//原tail处数据也已排好序,将其减1

			if(!flag) return;//本趟排序未发生交换,提前终止算法

	} 

} 

int main()
{
	int a[10]={5,8,4,78,12,96,1,4,16,50};
	int i;
	for(i=0;i<10;i++)
		cout<<a[i]<<" ";
	cout<<endl; 
	CocktailSort(a,10);
	for(i=0;i<10;i++)
		cout<<a[i]<<" ";
	cout<<endl; 

	system("pause");
	return 0;
}


 

三、快速排序算法

[算法思想]:快速排序法原理也是用了分治法,主要原理是将数组分为A[p..q-1] 和A[q+1..r],然后调整元素使得A[p..q-1]小于等于q,也小于等于A[q+1..r]。然后不断的递归,到最后就排序完成。

快速排序是一种排序算法,对包含n个数的输入数组,平均时间为O(nlgn),最坏情况是O(n^2)。通常是用于排序的最佳选择。因为,基于比较的排序,最快也只能达到O(nlgn)。

 快速排序方法1:

QUICKSORT(A, p, r)
         1  if p < r
         2    then q ← PARTITION(A, p, r)   //关键
         3         QUICKSORT(A, p, q - 1)
         4         QUICKSORT(A, q + 1, r)

数组划分
快速排序算法的关键是PARTITION过程,它对A[p..r]进行就地重排:
PARTITION(A, p, r)
1  x ← A[r]
2  i ← p - 1
3  for j ← p to r - 1
4       do if A[j] ≤ x
5             then i ← i + 1
6                  exchange A[i] <-> A[j]
7  exchange A[i + 1] <-> A[r]
8  return i + 1

例子如下:

 1、i p/j

  2   8   7   1   3   5   6   4(主元)
j指的2<=4,于是i++,i也指到2,2和2互换,原数组不变。
j后移,直到指向1..


2、j(指向1)<=4,于是i++
i指向了8,所以8与1交换。
数组变成了:
       i          j
  2   1   7   8   3   5   6   4
3、j后移,指向了3,3<=4,于是i++
i这是指向了7,于是7与3交换。
数组变成了:
             i         j
  2   1   3   8   7   5   6   4
4、j继续后移,发现没有再比4小的数,所以,执行到了最后一步,
即上述PARTITION(A, p, r)代码部分的 第7行。
因此,i后移一个单位,指向了8
                 i               j
  2   1   3   8   7   5   6   4
A[i + 1] <-> A[r],即8与4交换,所以,数组最终变成了如下形式,
  2   1   3   4   7   5   6   8
快速排序第一趟完成。


4把整个数组分成了俩部分,2 1 3,7 5 6 8,再递归对这俩部分分别快速排序。
i p/j
  2   1   3(主元)
  2与2互换,不变,然后又是1与1互换,还是不变,最后,3与3互换,不变,最终,3把2 1 3,分成了俩部分,2 1,和3。再对2 1,递归排序,最终结果成为了1 2 3。

7 5 6 8(主元),7、5、6、都比8小,所以第一趟,还是7 5 6 8,不过,此刻8把7 5 6 8,分成了  7 5 6,和8.[7 5 6->5 7 6->5 6 7],再对7 5 6,递归排序,最终结果变成5 6 7 8。

#include<iostream>
using namespace std;
int Partition(int A[],int p,int r)     //分治法  

{
	int i,j,x,temp;
	x=A[r];
	i=p-1;
	for(j=p;j<r;j++) 
	{
	
		if(A[j]<x)
		{
			temp=A[j]; 
			A[j]=A[++i];
			A[i]=temp;

		}
	}

	A[r]=A[++i];
	A[i]=x;
	return i;
}
void QuickSort(int A[],int p,int r)
{    
	int q;
	if (p<r)
	{
		q=Partition(A,p,r) ;
		QuickSort(A,p,q-1);
		QuickSort(A,q+1,r);
	}
	
} 

int main()
{
	int a[11]={5,8,4,78,12,56,96,1,4,16,50};
	int i;
	for(i=0;i<11;i++)
		cout<<a[i]<<" ";
	cout<<endl; 
	QuickSort(a,0,10);
	for(i=0;i<11;i++)
		cout<<a[i]<<" ";
	cout<<endl; 

	system("pause");
	return 0;
}

快速排序方法2:这个版本不再选取(如上第一版本的)数组的最后一个元素为主元,而是选择数组中的第一个元素为主元。

还是以上述相同的数组,应用此快排算法的版本二,来演示此排序过程:
这次,以数组中的第一个元素2为主元。
  2(主)  8  7  1  3  5  6  4

请细看:
  2  8  7  1  3  5  6  4
  i->                     <-j
   (找大)               (找小)
1、j
j找第一个小于2的元素1,1赋给(覆盖重置)i所指元素2
得到:
  1  8  7     3  5  6  4
      i       j
      
     
2、i
i找到第一个大于2的元素8,8赋给(覆盖重置)j所指元素(NULL<-8)
  1     7  8  3  5  6  4
      i   <-j

3、j
j继续左移,在与i碰头之前,没有找到比2小的元素,结束。
最后,主元2补上。
第一趟快排结束之后,数组变成:
  1  2  7  8  3  5  6  4

第二趟,
        7  8  3  5  6  4
        i->             <-j
         (找大)        (找小)
 
1、j
j找到4,比主元7小,4赋给7所处位置
得到:
        4  8  3  5  6  
        i->                j
2、i
i找比7大的第一个元素8,8覆盖j所指元素(NULL)
        4     3  5  6  8
            i          j
        4  6  3  5     8
            i->       j
                 i与j碰头,结束。
第三趟:
        4  6  3  5  7  8
......
以下,分析原理,一致,略过。
最后的结果,如下图所示:
  1  2  3  4  5  6  7  8

#include<iostream>
using namespace std;
int Partition(int A[],int p,int r)     //分治法  

{
	int i=p,j=r,x;
	x=A[p];
		while (i<j)
		{
           while (i<j&&A[j]>=x)
           {
			   j--;
           }
		   if (i<j)
		   {
			    A[i]=A[j];
		   }
		   while (i<j&&A[i]<=x)
		   {
			   i++;
		   }
		   if (i<j)
		   {
			   A[j]=A[i];
		   }
        

		}
		A[i]=x;

		return i;
}
void QuickSort(int A[],int p,int r)
{    
	int q;
	if (p<r)
	{
		q=Partition(A,p,r) ;
		QuickSort(A,p,q-1);
		QuickSort(A,q+1,r);
	}
	
} 

int main()
{
	int a[11]={5,8,4,78,12,56,96,1,4,16,50};
	int i;
	for(i=0;i<11;i++)
		cout<<a[i]<<" ";
	cout<<endl; 
	QuickSort(a,0,10);
	for(i=0;i<11;i++)
		cout<<a[i]<<" ";
	cout<<endl; 

	system("pause");
	return 0;
}


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值