排序算法02——插入排序(直接、折半)、快速排序

插入排序:

插入排序的思路就是,前面的数组已经有序(从第二个数看来,第一个数已经有序了,它只要找到自己的插入点插入就行了;然后第三个数看前两个数都已经有序了....以此类推),下标为i的这个值依次与前面的值比较,找到合适的地方就可以直接插入了。但是,数组的插入是需要插入位以后的数据全部后移,所以我们边比较边移位,这样才能在找到合适位置的情况下直接插入。

代码如下:

void insert_sort(int arr[],int len)
{
	for (int i = 0; i < len-1; i++) //把下标为i的元素插入到前面有序的数组中 
	{
		int num = arr[i+1];   //i=0时只有一个数肯定是有序的 因此从第二个数记录要插入的值  
		int j = 0;
		for (j = i;j>=0;j--)  
		{
			if (arr[j] > num)  //如果前面的数大于记录的值
			{
				arr[j+1] = arr[j]; //那么数往后移动 这里不用担心覆盖 因为i+1的值已经被记录在num里了
			}
			else
			{
				break; // arr[j]小于num  说明找到了可以插入的位置arr[j+1]
			}
		}
		arr[j+1] = num; //插入
	}
}

插入排序分直接插入和折半插入,时间复杂度都为O(n^2)

上面的就是直接插入 ,对于直接插入我们发现效率不是很高,每次要找到插入点都需要遍历前面的部分数组,运气好第一个就是,运气不好要遍历全部。因此,折半插入做到了优化,每次都与有序数组(这里与上述一样)的中间值比较,如果值小于中间值,说明要插入的地方在中间值左侧,反之则在右侧。

代码如下:

void binary_insertsort(int arr[],int len)
{
	for (int i = 0; i < len-1; i++)
	{
		int num= arr[i+1]; //插入数的下标从1开始
		int left = 0; //左区间
		int right = i; //右区间  [0,i]
		while(left <= right)
		{
			int mid = (left+right)/2; //中间值下标
			if (num < arr[mid])
			{
				right = mid-1; //小 说明插入位置在左侧 [0,mid-1]
			}
			else
			{
				left = mid+1; //大  说明在右侧 [mid+1,i]
			}
		}
		for (int j = i; j >= left ; j--)
		{
			arr[j+1] = arr[j]; //数据后移
		}
		arr[left] = num; //插入
	}
}


快速排序:

     快速排序的思想光是用文字描述有点吃力,所以我画了张图来便于理解。


这里需要几个参数 一个是pos(position,基准位置) 一个是 key (基准值) ,快速排序中有个基准值的概念,在当前循环下,基准值左边的数要小于基准值,右边的数要大于基准值。下面我用图来解释下这是如何实现的

首先,第一个基准值为3,下标为0。记key = 3; pos = 0; left ,right为当前需要排序的区间

第一次循环时,我们先从右边往左看,发现,右边第一个数(1)就小于key值(3),按照基准值的概念,右边的数都必须大于基准值,因此我们把1放到下标为pos的位置上,同时把pos赋值为1的下标7,即pos = 7 


然后我们从左边开始看,第一个数1小于key(3),符合基准值的概念,第二个数5大于key(3),不符合,因此我们就把5放到当前下标为pos的地方,同时pos也置为5的下标,即1。


此时发现右边的数都大于key值(3),左边的数都小于key值。那么我们就把key值放回下标为pos的位置上即可。


第一次循环结束 发现左边已经有顺序了 而右边还是比较乱 因此这里,我们用到了递归,递归调用本身来处理区间为[pos+1,right]的数组 ,(如果是左边无序,则是处理区间[left,pos-1],都乱的话那就都处理了)  

注意:快速排序的循环中仅仅只是更换pos的值,而key值在此次循环中一直保持不变,直到找到自己合适的地方放入后。通过递归调用来更换下一个key值。

代码如下:

void quickArr(int arr[],int left,int right)
{
	int pos = left; //left作为基准下标 使得基准左边皆小于基准,右边皆大于
	int key = arr[pos];
	int i=left;
	int j=right;
	while(j>i)
	{
		while(j>i && arr[j]>key)
		{
			j--;
		} //循环找到右边小于key的值
		if (j>i)
		{
			arr[pos] = arr[j];
			pos = j;
		}
		while(j>i && arr[i]<=key)  //=号放上面或下面都可以 
		{
			i++;
		} //循环找到左边大于key的值
		if (j>i)
		{
			arr[pos] = arr[i];
			pos = i;
		}
	}
	arr[pos] = key;
	if (pos-1 > left)
	{
		quickArr(arr,left,pos-1);
	}
	if (right>pos+1)
	{
		quickArr(arr,pos+1,right);
	}
}

最后在封装一下就ok了

void quickSort(int arr[],int len)
{
	quickArr(arr,0,len-1);
}

快速排序的时间复杂度为O(nlogn)   

计算过程: T(n)=2T(n/2)+n   n为第一次循环遍历所需   2T(n/2)表示递归所需的时间 

T(n/2)=2T(n/4)+n/2 .....  一直到T(2)= 2T(1)+2    

可以得到T(n) = 2T(n/2)+n=4T(n/4)+2n=8T(n/8)+3n=.....=nT(1)+n*logn  (T(1)为单位时间) 幂级数n小于nlogn 略去 

得到时间复杂度为O(nlogn)  log底数为2

猜你喜欢

转载自blog.csdn.net/canger_/article/details/80566457
今日推荐