第四次课 快速排序、归并排序、堆排序

快速排序、归并排序、堆排序
快速排序:时间上不稳定,空间O(1);
归并排序:时间稳定在nlogn,空间浪费大
堆排序:时间稳定在nlogn,空间O(1);
3.快速排序
24,26,31,78,88,99,32,-2,-1,1,57,11,13,15,18,21,35,59,62,65,68,69,71,73,75,3,5,8,38,45,48,49,52,55
优化版本从每组数里面的前三个选择中间的数作为基准数,未优化的就选取比较后分开的每组数的第一个数作为基准数就可以.
第一轮 排好一个数
3,5,8,-2,-1,1,11,13,15,18,21,24,#26#,31,78,88,99,32,57,35,39,62,65,68,69,71,73,75,38,45,48,49,52,55 O(n)
第二轮 排好两个数
-2,-1,1,3,#5#,8,11,13,15,18,21,24 ,#26#,31,32,57,35,39,62,65,68,69,71,73,75,38,45,48,49,52,55,#78#,88,99
第二轮 排好四个数
-2,3#-1#,1,3,#5#,8,#11#,13,15,18,21,24 ,#26#,31,#32#,57,35,39,62,65,68,69,71,73,75,38,45,48,49,52,55,#78#,#88#,99

需要进行k轮比较
20+21+22+…+2(k-1)=2^k-1=n/2–>logn
每次比较O(0)
nlogn

具体实现:
24,26,31,78,88,99,32,-2,-1,1,57,11,13,15,18,21,35,59,62,65,68,69,71,73,75,3,5,8,38,45,48,49,52,55
1>26比24大,让26和最末尾的55交换位置
2>55比24大,让55和倒数第二的52交换位置
。。。。。
遇到比24小的和24交换位置。

//快速排序的第一次操作,选择每组第一个数为基准数
#include <stdio.h>
void show(int* arr,int size)
{
	int i;
	for(i=0;i<size;i++)
		printf("%d ",arr[i]);
}
void m1(int* arr,int size)
{
	int left=1;int right=size-1;//基准右边一个数作为左边界,未交换过的最末尾的数是右边界
	int i;
	for(i=0;i<size-1;i++)//一共有n个数操作n-1次;
	{
		if(arr[left-1]<arr[left])//如果基准右边一个数(左边界)比基准大的话,将它和右边界交换位置,右边界左移
		{
			int w=arr[left];
			arr[left]=arr[right];
			arr[right]=w;
			right--;
		}
		else//如果左边界比基准小的话,让左边界和基准交换位置,左边界右移
		{
			int w=arr[left-1];
			arr[left-1]=arr[left];
			arr[left]=w;
			left++;
		}
	}
}
int main()
{
    int arr[]={24,26,31,78,88,99,32,-2,-1,1,57,11,13,15,18,21,35,59,62,65,68,69,71,73,75,3,5,8,38,45,48,49,52,55};
    int length=sizeof(arr)/4;
    m1(arr,length);
	show(arr,length);
	
	
   
    return 0;
}

//递归实现快速排序的完整代码
#include <stdio.h>
void show(int* arr,int size)
{
	int i;
	for(i=0;i<size;i++)
		printf("%d ",arr[i]);
}
void m1(int* arr,int left1,int right1)
{
	int left=left1+1;int right=right1;
	int i;
	while(left<=right)//和n-1次等价,反正运行n-1次也是让左边界刚好超过右边界
	{
		if(arr[left-1]<arr[left])
		{
			int w=arr[left];
			arr[left]=arr[right];
			arr[right]=w;
			right--;
		}
		else
		{
			int w=arr[left-1];
			arr[left-1]=arr[left];
			arr[left]=w;
			left++;
		}
	}
	if(right-1>left1)//递归的终止条件
		m1(arr,left1,right-1);
	if(right1>left)
		m1(arr,left,right1);
	/*if(right-1-left1>=1)//递归的终止条件
		m1(arr,left1,right-1);
	if(right1-left>=1)
		m1(arr,left,right1);*/
}
int main()
{
    int arr[]={24,26,31,78,88,99,32,-2,-1,1,57,11,13,15,18,21,35,59,62,65,68,69,71,73,75,3,5,8,38,45,48,49,52,55};
    int length=sizeof(arr)/4;
    m1(arr,0,length-1);
	show(arr,length);
	
	
   
    return 0;
}

写完感觉递归是一个比较难想明白的事情,而且在这个算法中个人感觉很多条件可以换个写法,比如标注出来的终止条件。而且游标的叫法有点多,换了一个写法并没有明显错误。2min写完。

4.归并排序
24,26,31,78,88,99,32,-2,-1,1,57,11,13,15,18,21,35,59,62,65,68,69,71,73,75,3,5,8,38,45,48,49,52,55
首先要了解一个合并有序序列
1,3,6,8,11,14,18,22,25,31
2,4,7,9,13,17,24

比较头部,每次选两个头部最小的,O(n)的时间复杂度就将其合并成一个有序序列了。

//归并有序序列的数组方法

#include <stdio.h>
void show(int* arr,int size)
{
	int i;
	for(i=0;i<size;i++)
		printf("%d ",arr[i]);
}
void m1(int *arr,int *brr,int *crr,int size1,int size2)
{
	int i=0,j=0;
	int index=0;
	while(1)
	{
			if(arr[i]<brr[j])
			{
				crr[index]=arr[i];
				i++;
				if(i>size1)//如果arr先遍历完
					break;
			}
			else
			{
				crr[index]=brr[j];
				j++;
				if(j>size2)//如果brr先遍历完
					break;
			}
				index++;
	}
	for(;i<size1;i++)//如果arr还有剩余
	{
		crr[index]=arr[i];
		index++;
	}
	for(;j<size2;j++)//如果brr还有剩余
	{
		crr[index]=arr[j];
		index++;
	}
}
int main()
{
    int arr[]={1,3,6,8,11,14,18,22,25,31};
	int brr[]={2,4,7,9,13,17,24};
	int crr[17];
    int length1=sizeof(arr)/4;
	int length2=sizeof(brr)/4;
    m1(arr,brr,crr,length1,length2);
	show(crr,17);
    return 0;
}

归并排序:
24,26,31,78,88,99,32,-2,-1,1,57,11,13,15,18,21,35,59,62,65,68 n个有序数组,每个数组长度为1
两两合并有序序列
24,26 31,38 88,99 -2,32 -1,1 11,57 11,13 15,18 21,35 59,62 65,68
n/2个有序数组 O(n)
四四合并
24,26,31,38 -2,32,88,99 -1,1,11,97 11,13,15,18 21,35,59,62 65,68
n/4个有序数组 O(n)
。。。。。。。
经过约logn轮,得到一个有序序列。
所以时间复杂度是 nlogn

每次从序列中取出两个数组,排列后放在一个新序列里然后拷贝回原序列

//归并有序序列的递归方法
#include <stdio.h>
#include <stdlib.h>
void show(int* arr,int size)
{
	int i;
	for(i=0;i<size;i++)
		printf("%d ",arr[i]);
}
void m1(int *arr,int left,int right,int *arr2)
{
	if(left>=right)
		return; //终止条件
	int mid=(left+right)/2;//将数组分为两个数组分别进行递归计算
	m1(arr,left,mid,arr2);
	m1(arr,mid+1,right,arr2);//递归算完之后得到了两个有序数组。
	int left1=left;
	int right1=mid;
	int left2=mid+1;
	int right2=right;
	int index=left;
	//下面是对两个有序数组的排序
	while(left1<=right1&&left2<=right2)
	{
			if(arr[left1]<arr[left2])
			{
				arr2[index]=arr[left1];
				index++;
				left1++;
			}
			else
			{
				arr2[index]=arr[left2];
				index++;
				left2++;
			}
	
	}
	while(left1<=right1)//如果前一半数组有剩余
	{
		arr2[index]=arr[left1];
		index++;left1++;
	}
	while(left2<=right2)//如果后一半数组有剩余,这里并不能简化,因为不知道哪个数组剩下的数多!
	{
		arr2[index]=arr[left2];
		index++;left2++;
	}
	int i;
	for(i=0;i<=right;i++)
	{
		arr[i]=arr2[i];
	}
}
int main()
{
    int arr[]={33,8,11,2,99,45,41,0,6,76,63,12,22,55,77,-12,-33,44,8,24,51,33,68,5};
	int length=sizeof(arr)/4;
	int* arr2=(int*)malloc(sizeof(arr));//申请一个和arr大小一样的空间用来排序
	m1(arr,0,length-1,arr2);
	show(arr,length);

    return 0;
}

二叉树
完全二叉树的特性:
1>完全二叉树用数组来存储
2>完全二叉树的下标有如下特点,左孩子一定是奇数,右孩子一定是偶数
3>左节点的下标=父节点的下标2+1,右节点的下标=父节点的下标2+2
大根堆:每个节点比孩子节点都要大
小根堆:每个节点比孩子节点都要小

33,8,11,2,99,45,41,0,6,76,63,12,22,55,77,-12,-33,44,8,24,51,33,68,5
无序数组构建大根堆
按照数组顺序插入,跟父节点进行比较,如果比父节点大,则和父节点的值进行交换。
如果总数有n个数,则形成的完全二叉树有logn层,每次新增一个数不超过logn次操作。
整体复杂度nlogn

大根堆构建有序数组
选树顶的挑出来,然后将树末尾的数补到最顶,然后和两个孩子进行比较,大的孩子放到最顶,逐渐下放,形成新的大根堆。
逐渐循环,直到树空,排序完成。拿走的最大数放到末尾,但不再计入完全树。
每次构建一次操作次数不超过logn,一共有n个数进行n次操作
整体复杂度nlogn

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值