c++实现快排、归并排序、插入排序

快速排序、归并排序、插入排序的c++实现

1.注释中的解释非常详细,建议直接复制代码到编译器中查看

2.头文件使用的是函数模板,可适用于各种数据类型的排序

头文件如下:

#pragma once
//internal sorting是数据直接就在内存中
//external sorting数据可能在文件中


//插入排序
//插入排序是从第二个元素开始一个个比较,每次仅考虑下标为i的元素应该在的位置,i以前的元素的位置是已经排好的
template <class E>
void swap(E a[], int i, int j)
{
	E tem = a[i];
	a[i] = a[j];
	a[j] = tem;
}

template <class E>
void insertionSort(E A[], int n)//升序排列
{
	for (int i = 1; i < n; i++)//从第二个开始检查,第一个是i=0
	{
		for (int j = i; j >0; j--)//从下标为i的元素开始倒着检查,检查每相邻的两个元素的大小
		//注意这得是j>0才行,j=0时候j-1越界
		{
			if (A[j] <A[j - 1])
				swap(A, j - 1, j);
			else
				break;//当满足A[j]>=A[j-1]时,关于j的循环就结束了
		}
	}
}

template<class E>
void inssort(E A[], int n)
{
	for (int i = 1; i < n; i++)
	{
		for (int j = i; (j > 0) && (A[j] < A[j - 1]); j--)//这里写的是做交换的条件,所以是A[j] < A[j - 1]
			swap(A, j - 1, j);
	}
}

template<class E>
void Bmergesort(E A[],E temp[],int left,int right)//为什么临时数组也要作为参数传入进去?这么做没有意义
{
	if (left == right)
		return;//这说明只有一个元素
	int mid = left + (right - left) / 2;
	Bmergesort(A, temp, left, mid);
	Bmergesort(A, temp, mid+1, right);

	for (int i = left; i <= right; i++)//这是把子数组复制到temp里,这里的left和right是每次传入的值
		temp[i] = A[i];

	//现在进行整合
	int i1 = left;
	int  i2 = mid + 1;
	for (int curr = left; curr <= right; curr++)
	{
		if (i1==mid+1)//这个时候左边那一半就已经全部算到A数组中了
		{
			A[curr] = temp[i2++];
		}
		else if(i2==right+1)
		{
			A[curr] = temp[i1++];
		}
		else if(temp[i1]<=temp[i2])
		{
			A[curr] = temp[i1++];//这时候说明下标i1的值更小一些,取i1
		}
		else
		{
			A[curr] = temp[i2++];//下标为i2的值更小一些
		}
	}
}

//优化的mergesort,优化的方法是不把数组分到一个一个元素,而是分成一些小段后用插入排序解决
template<class E>
void mergesort(E A[], E temp[], int left, int right)
{
	if (right - left <= 3)
	{
		insertionSort(&A[left], right - left + 1);
		return;
	}

	int mid = left + (right - left) / 2;
	mergesort(A, temp, left, mid);
	mergesort(A, temp, mid + 1, right);

	//merge部分,首先复制,这时候的复制把右一半倒过来复制,左边原样复制
	//把右边倒过来得原因是我们想让左半部分从小到大排列,右半部分从大到小排列,这样可以在两头相互比较
	//确定最终的结果,程序能运行到merge这个部分时,那说明分的部分已经做完了,这个时候每个小段大致为4个元素
	//因为存在元素总数不一定被4整除的情况,所以不一定都是4,这每一个小段都经历过插入排序,所以都是一个递增的顺序
	int i, j, k;
	for (i = mid; i >= left; i--)
		temp[i]=A[i];
	for (j = mid + 1; j <= right; j++)
		temp[right + mid + 1 - j]=A[j];
	//合的过程时间复杂度为O(n),因为i和j一共走了一遍
	for (i = left, j = right, k = left; k <= right; k++)
		//用三个参数的原因是ij控制temp,k控制A
	{
		if (temp[i] <=temp[j])
			A[k] = temp[i++];
		else
			A[k] = temp[j--];
	}
	//i j 两个指针向中间靠拢,可能出现交叉的情况,这个循环k决定了执行多少次,最后j+1=i
	//这个合的部分每次处理的都是两小块,每次函数返回到上一级时,都把两小块合成一大块
}

//快速排序
/*
divide:
选择总体中的任意一个元素作为pivot(中心点)
S1是S中所有小于等于pivot的元素
S2是S中所有大于等于pivot的元素
治:(conquer)
递归地对S1,S2排序
合combine the sorted S1, followed by v, followed by the sorted S2

分的策略:每一次划分只解决选为pivot的元素的位置
首先让所选的元素和最后一个元素交换位置
然后让i从第一个元素起,j从倒数第二个元素起 i = left, j = right – 1
当i的元素小于pivot时,i继续移动 j的元素大于pivot时,j继续移动,当i j都停下来的时候,交换i和j的元素
直到i和j交叉的时候,交换pivot的元素和i指向的元素,一次分的过程就完成了
*/
template<class E>
inline int partition(E A[], int l, int r, E& pivot)
{
	do //写这个函数一定要注意越界问题,就是这个地方的越界,考虑一些所选主元为最大值或最小值的情况
	{
		while ((l<=r)&&(A[l] <= pivot))//这里也会存在着越界的情况
			//PPT上为++l,但是在qsort传入时做了一点小改变,先按我的来
			l++;
		while ((l<=r)&&(A[r] >= pivot))//得保证不会越界,否则在pivot
			r--;
		if(l<r)//这句必须要加,否则在ij交叉的时候也会发生交换
			swap<E>(A, l, r);
	} while (l <= r);//这个地方=可以有可以没有,因为l=r的时候,上面两个while循环不能同时跳出来
	return l;//返回左边指针的位置,最后在交换时候要用到
}

template<class E>
inline int findpivot(E A[],int i,int j)//传入的参数是数组名及起始点和截止点
{
	return (i + j) / 2;
}

template<class E>
void Qsort(E A[], int i, int j)
{
	if (j <= i) return;
	int pivotindex = findpivot(A, i, j);
	swap<E>(A, pivotindex, j);//先把pivot放到最后面
	int k = partition<E>(A, i, j - 1, A[j]);//用函数模板要实例化呀!
	//这时候最后一个参数就是A[j]了,因为已经交换过了
	swap<E>(A, k, j);//再把pivot放回原来正确的位置
	Qsort<E>(A, i, k - 1);
	Qsort<E>(A, k+1, j);//第二个参数应传入k+1,这时候k的位置已经找到,不用再管它了
}

//书上的partition及qsort修正版
template<class E>
inline int Partition(E A[], int l, int r, E& pivot)
{
	do
	{
		while ((l < r) && A[++l] <= pivot);
		/*这个还必须是l小于r,考虑极端的情况{1,2,6,4,5,3}
		交换后主元最大,此时最开始r=5,l=-1,只有上面这个while运行了。(在Qsort里面swap后就是递增的序列)
		如果写等号,最后l就会为6,数组溢出了。
		*/
		while ((l < r) && pivot <= A[--r]);
		if(l<r)//这句真的可以不加
			swap<E>(A, l, r);
	} while (l < r);
	return l;
}

template<class E>
void QQsort(E A[], int i, int j)
{
	if (j <= i) return;
	int pivotindex = findpivot<E>(A, i, j);
	swap<E>(A, pivotindex, j);//先把pivot放到最后面
	//从这开始有点不同
	int  k = Partition<E>(A, i - 1, j, A[j]);
	//PPT这种写法只在Partition传入时候不一样,在下面递归调用QQSort时参数正常传入
	swap<E>(A, k, j);
	QQsort<E>(A, i, k - 1);
	QQsort<E>(A, k + 1, j);
}

测试程序:

// sorting.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
using namespace std;
#include"sort.h"

//#define S1
#ifdef S1
int main()
{
	int a[6] = { 9,8,7,6,5,4 };
	insertionSort<int>(a, 6);
	for (int i = 0; i < 6; i++)
		cout << a[i] << " ";
	cout << endl;
	int b[6] = { 9,8,7,6,5,4 };
	inssort(b, 6);
	for (int i = 0; i < 6; i++)
		cout << b[i] << " ";
	//插入排序成功
}
#endif // S1

//#define S2
#ifdef S2

int main()
{
	int a[6] = { 9,8,7,6,5,4 };
	int tem[6] = { 0 };
	mergesort<int>(a, tem, 0, 5);
	for (int m = 0; m < 6; m++)
		cout << a[m] << " ";
	cout << endl;
	int b[6]={ 9,8,7,6,5,4 };
	Bmergesort<int>(b, tem, 0, 5);
	for (int m = 0; m < 6; m++)
		cout << b[m] << " ";
	//mergesort成功
}
#endif // S2

int main()
{
	int a[6] = { 9,8,7,6,5,4 };
	Qsort<int>(a, 0, 5);
	for (int i = 0; i < 6; i++)
		cout << a[i] << " ";
	cout << endl;

	int b[6] = { 1,2,6,4,5,3 };//这是很特殊的一组数
	Qsort<int>(b, 0, 5);
	for (int i = 0; i < 6; i++)
		cout << b[i] << " ";
	cout << endl;

	int c[6] = { 1,3,5,2,7,4 };//{1,3,7,2,4,5}
	QQsort<int>(c, 0, 5);
	for (int i = 0; i < 6; i++)
		cout << c[i] << " ";
	cout << endl;

	int d[6] = { 1,2,6,4,5,3 };//这是很特殊的一组数
	QQsort<int>(d, 0, 5);
	for (int i = 0; i < 6; i++)
		cout << d[i] << " ";
	cout << endl;
	//perfect!!!
}
发布了9 篇原创文章 · 获赞 18 · 访问量 1528

猜你喜欢

转载自blog.csdn.net/bj_zhb/article/details/103602131
今日推荐