常用排序算法

冒泡排序:

一.冒泡排序算法及思路

冒泡排序(bubble sort)是一种交换排序,它的基本思想:两两比较相邻的关键字,如果反序则交换,直到没有反序的记录为止。

冒泡排序是稳定的排序算法。下面看思路:
1.初始状态下,arr[0,n-1]是无序的;
2.第一次扫描(遍历):从无序底部向上依次比较两个相邻的两个气泡的重量,若发现轻者在下,重者在上,则交换两者之间的位置。即依次比较(arr[n-1],arr[n-2]),(arr[n-2],arr[n-3]),…,(arr[1],arr[0])。对于每对气泡(arr[j+1],arr[[j]),如果arr[j+1]<arr[j],那么就交换两者的内容,也就是值。
第一次扫描结束,“最轻“的气泡就会浮到该区间的顶部,即关键字最小的记录被放在最高的位置arr[0]上。
3.第二次扫描:扫描arr[1…n-1]。扫描结束时,”次轻“的气泡漂浮到arr[1]位置上。
4.第i次扫描,arr[[0…i-1]和arr[i…n-1]分别是当前的有序区和无序区。扫描还是从无序区底部向上,直至该区顶部。扫描结束,该区中,最轻的气泡浮到顶部位置arr[i]位置上,结果是a[[0…i-1]变为新的有序区
最后,经过len-1次扫描可以得到有序区arr[0…n-1]。

二.代码实现

#include <iostream>
using namespace std;

//冒泡排序初级版本,又可称为交换排序 
void bubblesort1(int arr[],int len)
{
	//申请临时变量用于交换 
	int temp=0;
	//对数组进行长度为len-1次的扫描 
	for(int i=0;i<len-1;++i)
    {
    	//从后往前交换,这样最小值冒泡道开头部分 
		for(int j=i+1;j<=len-1;++j)
    	{
    	
			if(arr[j]<arr[i])
    		{
    			temp=arr[j];
    			arr[j]=arr[i];
    			arr[i]=temp;
			}
		}
	}
}

//优化后的冒泡排序,正宗的冒泡排序 
void bubblesort2(int arr[],int len)
{
	//申请临时变量用于交换 
	int temp=0;
	//对数组进行长度为len-1次的扫描 
	for(int i=0;i<len-1;++i) 
    {
    	
		//从后往前交换,这样最小值冒泡道开头部分 
		for(int j=len-1;j>=i;--j)
    	{
    		//如果后面一位的元素值小于当前元素值,交换两元素的值 swap(a[j],a[j+1])
		 
			if(arr[j+1]<arr[j])
    		{
    			temp=arr[j];
    			arr[j]=arr[j+1];
    			arr[j+1]=temp;
			}
		}
	}
}

void bubblesort3(int arr[],int len)
{
	//用于标记每次扫描时是否发生交换 
	int flag=0;
	//申请临时变量用于交换 
	int temp=0;
	//对数组进行长度为len-1次的扫描 
	for(int i=0;i<len-1;++i)
	{
		//每次交换之前要对flag置0操作 
		flag=0;
		for(int j=len-1;j>=i;--j)
		{
			//交换两者数据 swap(a[j],a[j+1]; 
			if(arr[j+1]<arr[j])
			{
				temp=arr[j+1];
				arr[j+1]=arr[j];
				arr[j]=temp;
				//发生交换flag置1操作 
				flag=1;
			}
			//这次扫面没有发生交换,说明已经是排序好的,不需要排序,返回 
			if(flag==0)
			{ 
				return;
			}
		} 
	}
 
} 
int main()
{
	int arr[]={3,7,5,8,1,9,2,6,4};
	cout<<"冒泡排序前:"<<endl;
	//	打印排序前的数组 printf_array(arr,9); 
	for(int i=0;i<9;++i)
	{
		cout<<arr[i]<<" ";
	} 
	cout<<endl;
	
	bubblesort1(arr,9);
	cout<<"冒泡排序后:"<<endl;
	//  打印排序后的数组printf_array(arr,9); 
	for(int i=0;i<9;++i)
	{
		cout<<arr[i]<<" ";
	} 
	cout<<endl;
	
	bubblesort2(arr,9);
	cout<<"冒泡排序后:"<<endl;
   //  打印排序后的数组printf_array(arr,9); 
   	for(int i=0;i<9;++i)
	{
		cout<<arr[i]<<" ";
	} 
	cout<<endl;
	
	bubblesort3(arr,9);
	cout<<"冒泡排序后:"<<endl;
   //  打印排序后的数组printf_array(arr,9); 
   	for(int i=0;i<9;++i)
	{
		cout<<arr[i]<<" ";
	} 
	cout<<endl;
	return 0;
}
  

冒泡排序

三.时间复杂度分析

最好情况:没有进行数据交换,O(N);

最坏情况:排序表是逆序的,O(N^2)

堆排序:

堆排序具有下列完全性质的完全二叉树:每个结点的值都大于或等于其左右孩子的结点的值称之为大顶堆;每个结点的值都小于其左右孩子结点的值称之为小顶堆。

小顶堆

                                 小顶堆

大顶堆

                               大顶堆

一. 排序算法及其思路:
堆排序(heap sort)就是利用堆(这里用大顶堆)进行排序的方法。基本思路是:将待排序的序列构成一个大顶堆。此时,整个序列的最大值就是堆顶的根结点。将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n-1个序列重新构成一个堆,这样就会得到n个元素的次大值。如此反复进行,便可以得到一个有序序列了。

二.代码实现
把堆进行向下调整的代码及图片:
堆排序自顶向下过程
图 堆排序自顶向下过程

堆排序自顶向下结果
图 堆排序自顶向下结果

//向下调整方法(自顶向下),堆排序完的结果不是二叉搜索树,存在右结点比左结点小,父结点是最小的
//传入一个需要向下调整的结点编号i,这里传入1, 即从堆的顶点开始 

void siftdown(int i)
{
	//flag是用来标及是否向下调整 
	int flag=0;
	int t=0;
	//当i结点有儿子时(至少有左儿子的情况下)并且有需要继续调整的时候,循环就执行 
	while(1*2<=n&&flag==0)
	{
		//首先判断它和左儿子的关系,并且用t记录值较小的结点编号
		if(h[i]>h[i*2])
		{
			t=i*2;	 
		}
		else
		{
			t=i;
		}
		//如果它有右儿子,再对右儿子进行讨论 
		if(i*2+1<=n)
		{
			if(h[t]>h[i*2+1])
			{
				t=i*2+1;
			}
		}
		//如果发现最小的结点编号不是自己,说明子结点中有比父结点更小的;意思就是父结点和左右孩子结点的值进行比较,最小的放在最上面 
		if(t!=i)
		{
			//申请临时变量用于交换,等同于swap(h[i],h[t])
			int temp=h[i];
		    h[i]=h[t];
			h[t]=temp;
			//更新i为刚才与他交换的儿子结点的编号,便于接下来继续向下调整 
			i=t;
		}
		else
		{
			//否则说明当前的父结点已经比两个子结点都要小了,不需要再进行调整了 
			flag=1;
		} 
	}
}

把堆进行向上调整的代码及图片:

堆排序自底向上过程
图 堆排序自底向上过程

堆排序自底向上结果
图 堆排序自底向上结果

//向上调整方法(自底向上),堆排序完的结果不是二叉搜索树,存在右结点比左结点小,父结点是最小的
//传入一个需要向上调整的结点编号i

void  siftup(int i) 
{
	//用来标记是否向上调整 
	int flag=0;
	int t=0;
	//如果i在堆顶就返回,不需要调整 
	if(i==1)
	{
		return;
	} 
	//如果i不在堆顶,并且当前结点的值比父亲结点的值还要小,就继续向上调整 
	while(i!=1&&flag==0)
	{
		//判断当前节点的值是否比父亲结点的值要小,如果小就交换,swap(h[i],h[i/2]) 
		if(h[i]<h[i/2])
		{
			int temp=h[i];
			h[i]=h[i/2];
			h[i/2]=temp;
		}
		else
		{
			//更改标识,表示不需要调整了,当前结点的值要比父亲结点的值要大 
			flag=1;
		}
		//更新编号i为父亲结点的编号,为了便于下一次继续向上调整 
		i=i/2;
	}
	return;
}

建立堆的代码:

1)自顶向下
//建立堆 自顶向下 
void creat()
{
	int n=0;
    for(int i=1;i<=m;++i)
    {
    	++n;
    	h[n]=a[i];  //或者可以这样写:scanf("%d",&h[n]); 
   	    siftup(n);
    }
    
}2)自底向上
//建立堆 自底向上 
void creat()
{	
	for(int i=n/2;i>=1;--i)
	{
		siftdown(i);
	}  
}	

删除最小元素的函数代码:

//删除最小元素的函数 
//可用于打印堆从小到大的排序 
int deletemin()
{
	//用临时变量来记录堆顶点的值 
	int temp=h[1];
	//把堆的最后一个结点的值赋值给堆顶点 
	h[1]=h[n];
	//堆的元素个数减1 
	--n;
	//向下调整 
	siftdown(1);
	//返回之前记录堆顶点最小的值 
	return temp; 
}

建立堆以及堆排序完整的代码如下:

//建立堆以及堆排序代码如下

#include <iostream>
using namespace std;
//用来存放堆的数组 
int h[101];
//用来存储堆堆中元素的个数,也就是堆的大小 
int n;


//交换函数,用来交换堆排序里面两个元素的值 
void swap(int x,int y)
{
	int temp=h[x];
	h[x]=h[y];
	h[y]=temp;
	return;
}


//向下调整函数(自顶向下) 
//传入一个需要向下调整的结点编号i,这里传入i,即从堆的顶点开始向下调整 
void siftdown(int i)
{
	//存放较小值得结点编号,交换元素的下标 
	int t;
	//flag是用来标记是否需要继续向下调整 
	int flag=0;
	//当i结点有儿子时(其实是至少有左儿子)并且有需要继续向下调整的时候循环就执行 
	while(i*2<=n&&flag==0)
	{
		//首先判断它和左儿子的关系,并用t记录值较小的结点编号 
		if(h[i]>h[i*2])
		{
			t=i*2;
		}
		else
		{
			t=i;	
		}
		//如果它有右儿子,再对儿子进行讨论 
		if(i*2+1<=n)
		{
			//如果右儿子的值更小,更新较小的结点编号 
			if(h[t]>h[i*2+1])
			{
				t=i*2+1;
			}
		}
		//如果发现最小的结点编号不是自己,说明子结点有比父结点更小的 
		if(t!=i)
		{
			//交换两者的值,swap(h[i],h[t]) 
			int temp=h[t];
			h[t]=h[i];
			h[i]=temp;
		}
		//否则说明当前的父结点已经比两个子结点都要小了,不需要再进行调整了 
		else
		{
			flag=1;
		}
	} 
	return;	
} 

//建立堆函数 
void creat()
{
    //从最后一个非叶结点到第1个结点依次进行向下调整 
	for(int i=n/2;i>=1;--i)
	{
		siftdown(i);
	}
	return;
}


//删除最小元素的函数 
//可用于打印堆从小到大的排序 
int deletemin()
{
	//用临时变量来记录堆顶点的值 
	int temp=h[1];
	//把堆的最后一个结点的值赋值给堆顶点 
	h[1]=h[n];
	//堆的元素个数减1 
	--n;
	//向下调整 
	siftdown(1);
	//返回之前记录堆顶点最小的值 
	return temp; 
}


int main()
{
	int i;
	int num;
	//输入元素的个数
	cout<<"请输入数字:"<<endl; 
	cin>>num; 
	//循环输入元素
	cout<<"请输入元素的数字"<<endl; 
	for(int i=1;i<=num;++i)
	{
		cin>>h[i];	
	}
	
	//输出没有堆排序的元素
	cout<<"输出堆排序之前的元素:"<<endl; 
	for(int i=1;i<=num;++i)
	{
		cout<<h[i]<<" ";	
	}
	cout<<endl;
	
	n=num;
	creat();
	//输出堆排序之后的元素
	cout<<"输出堆排序之后的元素:"<<endl; 
	for(int i=1;i<=num;++i)
	{
		cout<<deletemin()<<" "; 
	}
	cout<<endl;
	return 0;
}

堆排序
改进后的堆排序:
从小到大排序的时候不是建立最小堆而是最大堆,最大堆建立好之后,最大的元素再h[1],因为我们的需求是从小到大排序,最大的放在最后。所以我们将h[1]和h[n]交换,此时h[n]就是数组的最大元素。交换后,还需要把h[1]向下调整以保持堆的特性。循环反复,直到堆的大小变为1为止。此时数组h中的数就已经是排序好的了。
改进后的堆排序代码如下:

//建立堆以及改进后的堆排序代码如下

#include <iostream>
using namespace std;
//用来存放堆的数组 
int h[101];
//用来存储堆堆中元素的个数,也就是堆的大小 
int n;


//交换函数,用来交换堆排序里面两个元素的值 
void swap(int x,int y)
{
	int temp=h[x];
	h[x]=h[y];
	h[y]=temp;
	return;
}


//向下调整函数(自顶向下) 
//传入一个需要向下调整的结点编号i,这里传入i,即从堆的顶点开始向下调整 
void siftdown(int i)
{
	//存放较小值得结点编号,交换元素的下标 
	int t;
	//flag是用来标记是否需要继续向下调整 
	int flag=0;
	//当i结点有儿子时(其实是至少有左儿子)并且有需要继续向下调整的时候循环就执行 
	while(i*2<=n&&flag==0)
	{
		//首先判断它和左儿子的关系,并用t记录值较小的结点编号 
		if(h[i]>h[i*2])
		{
			t=i*2;
		}
		else
		{
			t=i;	
		}
		//如果它有右儿子,再对儿子进行讨论 
		if(i*2+1<=n)
		{
			//如果右儿子的值更小,更新较小的结点编号 
			if(h[t]>h[i*2+1])
			{
				t=i*2+1;
			}
		}
		//如果发现最小的结点编号不是自己,说明子结点有比父结点更小的 
		if(t!=i)
		{
			//交换两者的值,swap(h[i],h[t]) 
			int temp=h[t];
			h[t]=h[i];
			h[i]=temp;
		}
		//否则说明当前的父结点已经比两个子结点都要小了,不需要再进行调整了 
		else
		{
			flag=1;
		}
	} 
	return;	
} 

//建立堆函数 
void creat()
{
    //从最后一个非叶结点到第1个结点依次进行向下调整 
	for(int i=n/2;i>=1;--i)
	{
		siftdown(i);
	}
	return;
}


//堆排序
void heapsort()
{
	while(n>1)
	{
		swap(1,n);
		--n;
		siftdown(1);
	}
	return;
} 


int main()
{
	int i;
	int num;
	//输入元素的个数
	cout<<"请输入数字:"<<endl; 
	cin>>num; 
	//循环输入元素
	cout<<"请输入元素的数字"<<endl; 
	for(int i=1;i<=num;++i)
	{
		cin>>h[i];	
	}
	
	//输出没有堆排序的元素
	cout<<"输出堆排序之前的元素:"<<endl; 
	for(int i=1;i<=num;++i)
	{
		cout<<h[i]<<" ";	
	}
	cout<<endl;
	
	n=num;
	creat();
	//输出堆排序之后的元素
	cout<<"输出堆排序之后的元素:"<<endl; 
	for(int i=1;i<=num;++i)
	{
		cout<<h[i]<<" "; 
	}
	cout<<endl;
	return 0;
}

三.时间复杂度分析
最好情况:O(NlogN)
最坏情况:O(NlogN)
平均情况:O(NlogN)

快速排序
一.快速排序算法及思路:
快速排序(quick soer)的基本思想:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可份别对这两部分记录进行排序,已达到整个序列有序的目的。

三.代码实现

//快速排序
#include <iostream>
using namespace std;
void quicksort(int arr[],int left,int right)
{
	int i;
	int j;
	int pivot;
	if(left>right)
	{
		return;
	}

		
	if(left<right)
	{
		//pivot存基准数 
		pivot=arr[left];
		i=left;
		j=right;
		while(i<j)
		{
			//要从右边开始,这点很重要,别忽略了 
			while(i<j&&arr[j]>=pivot)
			{
				--j;
			}
			if(i<j)
			{
				//把比pivot小的元素移到左边 
				arr[i++]=arr[j];
			}
			while(i<j&&arr[i]<=pivot)
			{
				++i;
			}
			if(i<j)
			{
				//把比pivot大的元素移到右边 
				arr[j--]=arr[i];
			}
		}
		//pivot移到最终位置	
	    arr[i]=pivot;
	    //继续处理左边的,对左区间进行递归排序 
	    quicksort(arr,left,i-1); 
		//继续处理右边的,对右区间进行递归排序  
	    quicksort(arr,i+1,right);
	} 
}

int main()
{
	int arr[9]={6,2,7,9,3,1,5,4,8};
	//快速排序之前 
	cout<<"快速排序之前:"<<endl; 
	for(int i=0;i<9;++i)
	{
		cout<<arr[i]<<" ";
	}
	cout<<endl;
	
	quicksort(arr,0,8);
	//快速排序之前后 
	cout<<"快速排序之后:"<<endl; 
	for(int i=0;i<9;++i)
	{
		cout<<arr[i]<<" ";
	}
	cout<<endl;
}

快速排序结果
这里的pivot代表的是基准值,它的初始值为a[left]。局部变量i和j分别代表left和right的位置。接着按照下面的步骤进行一趟交换:
(1)把比pivot小的元素移到低端(left);
(2)把比pivot大的元素移到高端(high);
(3)pivot移到最终位置,此时这个位置的左边元素的值都要比pivot小,而右边的元素的值都要比pivot大。
(4)对左边,右边分别进行递归排序。从而把前三步的排序慢慢细化,直到left和right交汇。

三.时间复杂度分析
最好:O(NlogN)
最坏:O(N^2)
平均:O(NlogN)

归并排序
一.归并排序的算法及思路:
归并排序(merge sort)是利用归并的思想实现的排序的方法。它的原理是假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到[n/2],([x表示不小于x的最小整数)个长度为2或1的有序子序列;再两两归并, … ,如此重复,直至得到一个长度为n的有序序列为止,这种方法成为二路归并排序。

二.代码实现:

//归并排序
#include <iostream>
using namespace std; 

void merge(int a[],int temp[],int left,int mid,int right)
{
    int i=left;
    int j=mid+1;
    int k=left;
    //比较两边的数组,把较小的数组元素的值放入临时数组temp中
    while (k<=right) 
	{
        if(i>mid) 
		{
            temp[k++]=a[j++];
        }
        else if(j>right) 
		{
            temp[k++]=a[i++];
        }
        else 
		{
            //把右边剩余的元素放进temp数组中
            if(a[i]>a[j]) 
			{
                temp[k++]=a[j++];
            }
            //把左边剩余的元素放进temp数组中
            else 
			{
                temp[k++]=a[i++];
            }
        }
    }
    //把临时数组复制到原始数组
    for(int k=left;k<=right;k++) 
	{
        a[k]=temp[k];
    }
}

void merge_sort_helper(int a[],int temp[],int left,int right)
{
    //结束条件,原子结点return
    if(left>=right) 
	{
        return;
    }
    //计算分裂点
    int mid=left+(right-left)/2;
    //对左区间用递归做归并排序
    merge_sort_helper(a,temp,left,mid);
    //对左区间用递归做归并排序
    merge_sort_helper(a,temp,mid+1,right);
    //组合,把左右两个有序区间合并为一个有序区
    merge(a,temp,left,mid,right);
}

void merge_sort(int a[], int len)
{
    //分配临时数组
    int *temp=new int[len];
    //调用merge_sort_helper函数进行归并排序
    merge_sort_helper(a,temp,0,len-1);
    //释放临时数组内存
    delete[] temp;
}

int main(int argc, const char * argv[])
{
    int a[9]= {3,5,7,2,8,9,1,6,4};
    cout<<"归并排序前:"<<endl; 
    for(int i=0;i<9;++i)
    {
    	cout<<a[i]<<" ";	
	}
	cout<<endl; 
    
    merge_sort(a,9);
    cout<<"归并排序后:"<<endl;  
    for(int i=0;i<9;++i)
	{
        cout<<a[i]<<" ";
    }
    cout<<endl;
    
    return 0;
}

非递归排序代码如下:


```cpp
//非递归归并排序算法,自底向上 
#include<iostream>

using namespace std;
//在原始数组上进行操作,
void merge(int arr[], int low, int mid, int high)
{ 
    int i,j,k;
    //low为第一个有序区的第一个元素
    i=low;
    //mid+1为第二个有序区的第一个元素
    j=mid+1; 
    k=0;
    int *temp=new(nothrow)int[high-low+1];
    if(!temp)
    {	
        cout << "内存分配失败"<< endl;
        return;
    }
    //依次比较两个有序序列的第一个元素,将较小的一方存放到temp数组中
    while(i<=mid&&j<=high)
    {	
        if(arr[i]<arr[j])
        {
        	temp[k++] = arr[i++];
		} 
        else
        {
        	temp[k++] = arr[j++];
		}   
    }
    while (i <= mid)
    temp[k++] = arr[i++];
    while (j <= high)
    temp[k++] = arr[j++];
    //将排好序的存回arr中low到high该区间内
    for(i=low,k=0;i<= high;i++,k++)
    arr[i] = temp[k];
    //删除指针,由于指向的是数组,必须用delete[]
    delete[]temp;
}
 
//非递归合并排序函数:根据步长size大小先处理数组中两两相邻的序列,合并后成倍扩大size,进入下一轮的合并排序 
void mergeSort(int arr[], int n)
{
    //非递归法实现归并排序, 形参n表示为数组中的元素个数
    int size=1,low,mid,high;
    while(size <= n)
    {
        low=0;
        while (low+size<=n)
        {
            mid=low+size-1;
            high=mid+size;
            if (high>n)
            {
            	high=n;
			}
            merge(arr,low,mid,high);
            low=high+1;
        }
        size=size*2;
    }
 
}


int main()
{
    int a[9]={4,2,7,5,8,9,3,1,6};
    int n=sizeof(a)/sizeof(int);
    //非递归归并排序前
	cout<<"非递归归并排序前:"<<endl; 
    for (int i=0;i<n;i++) 
    {
    	cout << a[i] << " ";
	}
    cout<<endl;
    
    mergeSort(a,n-1);
    //非递归归并排序后 
	cout<<"非递归归并排序后:"<<endl; 
    for (int i=0;i<n;i++)
    {
    	cout << a[i] << " ";
	}
    cout<<endl;
    return 0;
}

三.时间复杂度分析
最好:O(NlogN)
最坏:O(NlogN)
平均:O(NlogN)

桶排序
一.桶排序的算法及思路:
桶排序(bucket sort),基本思想是:设置若干个桶子,依次扫描待排序的记录A[0],A[1], … ,A[n-1],把关键字等于k的记录全都装入到第k个箱子里(分配),然后按照序号依次将非空的桶子依序连接起来(收集)。

二.代码实现

//桶排序计数方法 
#include <iostream>
using namespace std;
int main()
{
	int a[9];
	int t;
	for(int i=0;i<9;++i)
	{
		//对数字出现的次数全部初始化为0 
		a[i]=0;
	}
	//循环读入9个数 
	for(int i=0;i<9;++i)
	{
		//把每一个数读进变量t中 
		cin>>t;
		//进行计数 
		a[t]++; 
	}
	//依次判断a[0]至a[9] 
	for(int i=0;i<9;++i)
	{
		//c出现几次就打印几次 
		for(int j=0;j<a[i];++j)
		{
			cout<<i<<" "; 
		}
	}
	
	return 0;
}

桶排序结果
三.时间复杂度分析
最好:O(N)
最坏:O(NlogN)
平均:O(N+M)

选择排序
一.选择排序的算法及思路
直接插入排序是稳定的排序算法。直接插入排序的基本思想:假设待排序的记录存放再数组A[0…n-1]中。初始时,A[0]自成一个排序区,无序区为R[2…n]。从i=1起直到i=n-1为止,依次将A[I]插入当前对的有序区R[0…I-1] 中,生成含n个记录的有序区。

二.代码实现

//直接插入排序算法 
#include <iostream>
using namespace std;

void insertsort(int a[],int len)
{
	int temp;
	int j,i;
	//需要选择len-1次 
	for(i=1;i<len;++i)
	{
		//暂时存放下标为i的数。下标从1开始,因为开始时,下标为0的数,前面没有任何数,此时被认为是排好序的数 
		temp=a[i];
		for(j=i-1;temp<a[j]&&j>=0;--j)
		{
			//如果条件满足就往后挪。最坏的情况是temp比a[0]小,它要放在最前面 
			a[j+1]=a[j];
		}
		//找到下标为i的数的存放位置 
		a[j+1]=temp;
	}
}

int main()
{
	int a[9]={3,7,1,8,4,9,5,6,2};
	//直接插入排序前
	cout<<"直接插入排序前:"<<endl; 
	for(int i=0;i<9;++i)
	{
		cout<<a[i]<<" ";
	}
	cout<<endl;
	
	insertsort(a,9);
	//直接插入排序后 
	cout<<"直接插入排序后:"<<endl;
	for(int i=0;i<9;++i)
	{
		cout<<a[i]<<" ";
	}
	cout<<endl;
	
	return 0;	 
}

直接插入排序结果
三.时间复杂度分析
最好:O(N)
最坏:O(N^2)
平均:O(N^2)

希尔排序
一.希尔排序的算法及思路
希尔排序算法(shell sort)是属于插入排序,是不稳定的排序方法。
希尔排序基本思想:希尔排序算法先将要排序的一组数按照某个增量d分成若干组,每组中记录的下标差d对每组全部元素进行排序,然后用一个较小的增量对它进行再次分组,并对每个新数组重新进行排序。当增量减到1时,整个要排序的数被分成一组,排序完成。所以,希尔排序属于一种的分组插入方法。

二.代码实现

//希尔排序算法实现 
#include <iostream>
using namespace std;
void shellsort(int a[],int len)
{
	int i;
	int j;
	int temp;
	int t;
	//控制增量 
	for(t=len/2;t>0;t=t/2)
	{
	
		//这个for循环就是前面的直接插入排序 
		for(i=t;i<len;++i)
		{
			temp=a[i];
			for(j=i-t;j>=0&&temp<a[j];j=j-t)
			{
				a[j+t]=a[j];
			}
			a[j+t]=temp;
		}
	}
} 


int main()
{
	int a[9]={4,3,5,2,9,1,7,6,8};
	//希尔插序前
	cout<<"希尔排序前:"<<endl; 
	for(int i=0;i<9;++i)
	{
		cout<<a[i]<<" ";
	}
	cout<<endl;
	
	shellsort(a,9);
	//希尔排序后 
	cout<<"希尔排序后:"<<endl;
	for(int i=0;i<9;++i)
	{
		cout<<a[i]<<" ";
	}
	cout<<endl;
	
	return 0;
}

三.时间复杂度分析:
最好:O(Nlog n~N^2)
最坏:O(N^1.5)
平均:O(N^2)

选择排序
一.选择排序的算法及思路
选择排序(select sort),就是通过n-i次关键字的比较,从n-i+1个记录中选出关键字最小的记录,并和第i(1<=i<=n)个记录交换之。

二.代码实现

#include <iostream>
using namespace std;

void selectsort(int a[],int len)
{
	int min;
	for(int i=0;i<len;++i)
	{
		//将当前下标定义为最小值下标 
		min=i;
		//循环之后的数据 
		for(int j=i+1;j<len;++j)
		{
			//如果有小于当前最小值的关键字 
			if(a[min]>a[j])
			{
				//将关键字的下标赋值给min 
				min=j;
			}
		}
		//如果i不等于i,说明找到最小值,交换两者的值,swap(a[i],a[min]) 
		if(i!=min)
		{
			int temp=a[i];
			a[i]=a[min];
			a[min]=temp;
		} 
	}
}

int main()
{
	int a[9]={5,3,7,6,1,9,4,2,8};
	//选择插序前
	cout<<"选择排序前:"<<endl; 
	for(int i=0;i<9;++i)
	{
		cout<<a[i]<<" ";
	}
	cout<<endl;
	
	selectsort(a,9);
	//选择排序后 
	cout<<"选择排序后:"<<endl;
	for(int i=0;i<9;++i)
	{
		cout<<a[i]<<" ";
	}
	cout<<endl;
	
	return 0;	
} 

选择排序结果
三.时间复杂度分析
最好:O(N^2)
最坏:O(N^2)
平均:O(N^2)

排序算法总结

排序方法最好情况最坏情况平均情况空间复杂度稳定性
冒泡排序O(N)O(N^2)O(N^2)O(1)稳定
堆排序O(NlogN)O(NlogN)O(NlogN)O(1)不稳定
快速排序O(NlogN)O(N^2)O(NlogN)O(logN)~O(N)不稳定
归并排序O(NlogN)O(NlogN)O(NlogN)O(n)稳定
桶排序O(N)O(NlogN)O(N+M)O(1)稳定
直接插入排序O(N)O(N^2)O(N^2)O(1)稳定
希尔排序O(N^1.5)O(N^2)O(NlogN)~O(N^2)O(1)不稳定
选择排序O(N^2)O(N^2)O(N^2)O(1)不稳定
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值