数据结构之排序2 ---(归并排序,堆排序,快速排序)

数据结构之排序  

目录

归并排序:

堆排序:

快速排序:


归并排序:

  • 归并排序采用分治法,将元素递归分成若干组,排好序后再逐级合并(Merge过程最坏情况下的比较次数为2n-1)
  • 归并排序最好、最坏、平均复杂度都一样,比较占用空间,但却是稳定高效的排序算法,仅次于QuickSort
  • 时间复杂度:O(nlgn)
  • 空间复杂度:O(n)
  • stable:是
void merge(int *array, int first, int center, int end)
{
	int n1 = center - first + 1;
	int n2 = end - center;

	int *L = new int[1 + n1]; //用动态数组储存,不然会出错的
	int *R = new int[1 + n2];
	int i, j;
	for (i = 0; i < n1; i++)
	{
		L[i] = array[first + i];
	}
	for (j = 0; j < n2; j++)
	{
		R[j] = array[center + j + 1];
	}
	L[n1] = INT_MAX;  //设置哨兵的判断一个结束后,剩余全部放入。 
	R[n2] = INT_MAX;
	i = j = 0;
	for (int k = first; k <= end; ++k)
	{
		if (L[i] < R[j])
		{
			array[k] = L[i];
			i++;
		}
		else
		{
			array[k] = R[j];
			j++;
		}
	}
}

void merge_sort(int array[], int first, int end)
{
	if (first < end)
	{
		int center = (first + end) / 2;
		merge_sort(array, first, center);
		merge_sort(array, center + 1, end);
		merge(array, first, center, end);
	}

}

void testMergeSort()
{
	int array[10] = { 0,6,1,2,4,7,8,9,3,5 };
	cout << "arr is old :";
	for (int i = 0; i < 10; i++)
	{
		cout << array[i] << " ";
	}
	cout << endl;
	merge_sort(array, 0, 9);  //排序后
	cout << "arr is new :";
	for (int i = 0; i < 10; i++)
	{
		cout << array[i] << " ";
	}
	cout << endl;

}

 结果:

结果:
arr is old :0 6 1 2 4 7 8 9 3 5
arr is new :0 1 2 3 4 5 6 7 8 9

堆排序:

  • 通过数据结构堆,来将插入排序和归并排序的优点结合起来,是一种渐进最优的排序
  • 时间复杂度:O(nlgn)
  • 空间复杂度:O(1)
  • stable:否

0、(二叉)堆数据结构是一种数组对象,可以被视为一颗完全二叉树
1、对于从0开始的数组,给定一个结点下标i,其父结点的下标为(i-1)/2,左子树的下标为i*2+1,右子树的下标为i*2+2
2、可以通过左移/右移1位再+1/-1的方法来快速计算出父/子结点
3、二叉堆有两种,最大堆和最小堆
最大堆:某个结点的值至多和其父结点的值一样大;这样,堆中的最大元素就存储在根结点中
最小堆:和最大堆正相反
在堆排序算法中使用最大堆,在优先级队列中使用最小堆
4、堆可以被视为一棵树:
结点在树中的高度:从本结点到叶子的最长简单下降路径上结点的数目;因为具有n个元素的堆是基于一棵完全二叉树的,因此其高度为lgn
结点在树中的深度:从本结点到根结点的最长简单上升路径上结点的数目
5、因为二叉堆的性质,数组的后二分之一元素都是叶子
6、在一个含有n个元素的二叉堆中,至多有n/(2的h次幂)个高度为h的结点(叶子高度为1)
7、完全二叉树:
所有叶子都在同一层且并不是满的,缺少右边的叶子(如果缺少的叶子的右边还有叶子,那么就不是完全二叉树)
满二叉树:
国内定义:
所有叶子都在同一层且刚好是满的
国外定义:
所有结点,要么没有子结点(指叶子),要么有两个子结点
满二叉树是完全二叉树的一个特例
---------------------------
8、优先级队列:
堆的一个很常见的应用:
作为高效的优先级队列;像堆一样,优先级队列也分为两种:最大优先级队列和最小优先级队列
最大优先级队列的一个应用是分时计算机上进行作业调度;这种队列对要执行的各作业及它们之间的优先关系先加以记录,当上一个作业完成时,从所有等待的作业中,选择出具有最高优先级的作业执行
最小优先级队列的一个应用是基于事件驱动的模拟器中,每一个都有时间做为其关键字;事件模拟要按时间的发生顺序进行,最小优先级队列比较合适
9、优先级队列返回并去掉其根结点后,将数组最后一个元素交换到原根结点位置,而不是所有元素都移动

void maxHeapify(int ary[], int heapSize, int index)
{
	int left = 2 * index + 1;    //此函数是有前提的:以left和right为根的子树是最大堆
	int right = 2 * index + 2;
	int largest = index;
	if (left <= heapSize - 1 && ary[left] > ary[largest])
		largest = left;
	if (right <= heapSize - 1 && ary[right] > ary[largest])
		largest = right;
	if (largest != index)    //largest是leftchild和rightchild中最大的
	{
		swap(ary[index], ary[largest]);
		maxHeapify(ary, heapSize, largest);
	}    //将父结点交换到子结点后,继续检查该结点的子结点是否符合最大堆
}
void BuildmaxHeap(int ary[], int heapSize)    //构建最大堆
{    //因为后二分之一都是叶子没有子结点,所以从heapSize/2开始
	for (int i = (heapSize - 1) / 2; i >= 0; --i)
		maxHeapify(ary, heapSize, i);    //从叶子到根结点
}
void HeapSort(int ary[], int heapSize)
{
	BuildmaxHeap(ary, heapSize);
	for (int i = heapSize - 1; i > 0; --i)
	{
		swap(ary[0], ary[i]);
		maxHeapify(ary, i, 0);    //堆大小逐渐减小,去掉最后面的排好序的元素
	}
}

void testHeapSort()
{
	int array[10] = { 0,6,1,2,4,7,8,9,3,5 };
	cout << "arr is old :";
	for (int i = 0; i < 10; i++)
	{
		cout << array[i] << " ";
	}
	cout << endl;
	HeapSort(array, 10);  //排序后
	cout << "arr is new :";
	for (int i = 0; i < 10; i++)
	{
		cout << array[i] << " ";
	}
	cout << endl;
}

结果:

结果:
arr is old :0 6 1 2 4 7 8 9 3 5
arr is new :0 1 2 3 4 5 6 7 8 9

 快速排序:

  • 快速排序是采用分治法的就地排序,最坏运行时间为O(n*n),虽然最坏运行时间比较差,但快速排序仍然是排序的最佳实用选择,因为其平均性能相当好:期望时间复杂度为O(nlgn),且O(nlgn)中的常数因子很小
  • 快速排序的运行时间与划分是否对称有关,而后者又与选择了哪个元素来进行划分有关;如果划分是对称的,则与合并算法一样快;如果不对称就与插入排序一样慢
  • 时间复杂度:平均O(nlgn),最坏O(n*n)
  • 空间复杂度:O(1)
  • stable:否 
int Partition(int ary[], int left, int right)
{
	int i = left - 1;    //i的起始位置在left和j的前一位
	for (int j = left; j < right; ++j)
		if (ary[j] <= ary[right])    //主元设为当前区间最后一个元素,pivot=ary[right]
		{
			++i;
			swap(ary[i], ary[j]);
		}
	swap(ary[i + 1], ary[right]);    //将主元从最后交换到i的下一位
									 //因为0~i都是小于主元的
									 //所以交换后主元的位置已经是排好序的位置
	return i + 1;    //返回当前主元的位置
}
void QuickSort(int ary[], int left, int right)
{
	if (left < right)
	{
		int pivot = Partition(ary, left, right);
		QuickSort(ary, left, pivot - 1);    //因为主元位置已排好,所以跳过上次主元位置
		QuickSort(ary, pivot + 1, right);
	}
}

void testQuickSort()
{
	int array[10] = { 0,6,1,2,4,7,8,9,3,5 };
	cout << "arr is old :";
	for (int i = 0; i < 10; i++)
	{
		cout << array[i] << " ";
	}
	cout << endl;
	QuickSort(array,0, 9);  //排序后
	cout << "arr is new :";
	for (int i = 0; i < 10; i++)
	{
		cout << array[i] << " ";
	}
	cout << endl;
}

 测试结果:

结果:
arr is old :0 6 1 2 4 7 8 9 3 5
arr is new :0 1 2 3 4 5 6 7 8 9

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值