【数据结构】排序算法总结和优化以及测试函数

测试用的函数

命名空间的名称 SortTestHelper

生成随机数组

	int* generateRandomArray(int n, int rangeL, int rangeR)
	{
		assert(rangeL <= rangeR);
		int* arr = new int[n];
		srand(time(NULL));//生成随机种子
		for (int i = 0;i < n;i++)
		{
			arr[i] = rand() % (rangeR - rangeL + 1) + rangeL;//生成随机数[l,r]闭区间
		}
		return arr;
	}

生成近乎有序的随机数组

	int* generateNearlyRandomArray(int n, int swaptimes)
	{
		int* ret = new int[n];
		for (int i = 0;i < n;i++)
		{
			ret[i] = i;
		}
		srand(time(NULL));
		for (int i = 0;i < swaptimes;i++)
		{
			int posx = rand() % n;
			int posy = rand() % n;
			swap<int>(posx, posy);
		}
		return ret;
	}

打印数组

	void printArray(int* array, int n)
	{
		for (int i = 0;i < n;i++)
		{
			cout << array[i] << " ";
		}
		cout << endl;
	}

判断是否完成

	template<typename T>
	bool isSorted(T array[], int n)
	{
		bool sorted = true;
		for (int i = 0;i < n-1;i++)
		{
			if (array[i] > array[i + 1])return false;
		}
		return sorted;
	}

测试时间

	template<typename T>
	void testSortTime(string Sortname, void(*sort)(T array[], int n), T array[], int n)
	{
		clock_t startTime = clock();
		sort(array, n);
		clock_t endTime = clock();
		assert(isSorted<T>(array, n));
		cout << Sortname << " " << double(endTime - startTime)/CLOCKS_PER_SEC<<"S" << endl;
	}

复制数组

	int* copyArray(int a[], int n)
	{
		int* arr = new int[n];
		copy(a, a + n, arr);
		return arr;
	}

定义不同的数据类型进行测试

定义学生类型并为了方便比较进行操作符重载

struct Student {
	
	string name;
	int score;
	bool operator<(const Student& other)
	{
		return(score < other.score);
		return(score = other.score ? name < other.name : score < other.score);//自定义的排序方式 根据名字和分数排序
	}
	friend ostream& operator<<(ostream& out, const Student& student)//返回类型为引用
	{
		out << "student"<<" "<<student.name << " " << student.score << endl;
		return out;
	}
};

测试函数 主函数

int main()
{
	int n = 50000;
	//普通排序
	cout << "普通排序" << endl;
	int* array = generateRandomArray(n,0,n);
	int* array1=copyArray(array, n);
	int* array2 = copyArray(array, n);
	int* array3 = copyArray(array, n);
	testSortTime("bubbleSort", bubbleSort<int>, array, n);
	testSortTime("selectSort", selectionSort<int>, array1, n);
	testSortTime("insertSort", insertSort2<int>, array2, n);
	//testSortTime("mergeSort", mergeSort<int>, array1, n);
	//testSortTime("quickSort", quickSort<int>, array2, n);
	testSortTime("quickSort2", quickSort2<int>, array3, n);
	testSortTime("quickSort3", quickSort3<int>, array, n);
	//testSortTime("mergeSortBU", mergeSortBU<int>, array2, n);
	delete[] array1;//释放内存
	delete[] array2;
	delete[] array3;//释放内存
	delete[] array;
	//大量重复
	cout << "大量重复" << endl;
	array = generateRandomArray(n, 0, 10);
	array1 = copyArray(array, n);
	array2 = copyArray(array, n);
	array3 = copyArray(array, n);
	testSortTime("mergeSort", mergeSort<int>, array1, n);
	testSortTime("quickSort", quickSort<int>, array2, n);
	testSortTime("quickSort2", quickSort2<int>, array3, n);
	testSortTime("quickSort3", quickSort3<int>, array, n);
	delete[] array1;//释放内存
	delete[] array2;
	delete[] array3;//释放内存
	delete[] array;
	//近乎排序完成
	cout << "近乎排序完成" << endl;
	array = generateNearlyRandomArray(n, 10);
	array1 = copyArray(array, n);
	array2 = copyArray(array, n);
	array3 = copyArray(array, n);
	testSortTime("mergeSort", mergeSort<int>, array1, n);
	testSortTime("quickSort", quickSort<int>, array2, n);
	testSortTime("quickSort2", quickSort2<int>, array3, n);
	testSortTime("quickSort3", quickSort3<int>, array, n);
	delete[] array1;//释放内存
	delete[] array2;
	delete[] array3;//释放内存
	delete[] array;
	//selectionSort<int>(array1, n);
	//printArray(array1, n);
	//int array[10] = { 5,8,6,2,10,1,3,7,9,4 };
	//Student d[4] = { {"D",90},{"C",70} ,{"A",100} ,{"B",85} };
	//selectionSort<Student>(d,4);
	//selectionSort<int>(array, 10);
	delete[] array1;//释放内存
	delete[] array;
}

冒泡排序

如果一次遍历中没有发生交换 说明已经排好序了 可以提前结束

template <typename T>
void bubbleSort(T array[], int n)
{
	int already=0;
	int i = 0;
	while(!already)
	{
		already = 1;
		for (int j = 0;j < n - i-1;j++)
		{
			if (array[j] > array[j + 1])
				sswap(&array[j], &array[j + 1]);
			already = 0;
		}
		i++;
	}
}

选择排序

找到每一行的最小数,然后和起始位置进行交换

template <typename T>
void selectionSort(T array[], int n)
{
	for (int i = 0;i < n;i++)
	{
		int minIndex = i;
		for (int j = i+1;j < n;j++)
		{
			if (array[j] < array[minIndex])
			{
				minIndex = j;
			}
		}
		swap<T>(array[i], array[minIndex]);
	}
}

插入排序

原始方式 每次都进行交换

template <typename T>
void insertSort(T array[], int n)
{
	for (int i = 1;i < n;i++)
	{
		//寻找合适的插入位置 一个换一个
		for (int j = i;j > 0 && array[j] < array[j-1];j--)
			swap<T>(array[j-1], array[j]);
	}
}

优化的方式

先把元素后移,找到合适的位置直接插入

template <typename T>
void insertSort2(T array[], int n)
{
	for (int i = 1;i < n;i++)
	{
		T tmp = array[i];
		int j=0;
		//寻找合适的插入位置 一个换一个
		for (j = i;j > 0 && tmp < array[j - 1];j--)
			array[j] = array[j - 1];
		array[j] = tmp;
	}
}

其他函数调用时进行参数修改

快速排序和归并排序在尺寸较小时 可以使用插入排序

template <typename T>
void insertSort2(T array[], int n)
{
	for (int i = 1;i < n;i++)
	{
		T tmp = array[i];
		int j=0;
		//寻找合适的插入位置 一个换一个
		for (j = i;j > 0 && tmp < array[j - 1];j--)
			array[j] = array[j - 1];
		array[j] = tmp;
	}
}

归并排序

原始方式

主函数

template<typename T>
void mergeSort(T array[], int n)
{
	__mergeSort(array,0, n - 1);
}

递归调用排序函数

template<typename T>
void __mergeSort(T array[],int left,int right )
{	
	if (left >= right)
	{
		return;
	}/*
	if (right - left <= 15)//优化2 数组比较小的时候使用插入排序然后记得返回
	{
		insertSort<T>(array, left, right);
		return;
	}*/
	int midle = left + (right - left) / 2;//避免数太大越界
	__mergeSort(array, left, midle);
	__mergeSort(array, midle + 1, right);
	//if(array[midle]>array[midle+1])//优化 当左边最后一个值大于右边第一个值才merge
		__merge(array, left, right, midle);
}

merge函数

template<typename T>
void __merge(T array[], int left, int right, int midle)
{
	T* tmp = new T[right - left + 1];//新建一个数组
	for (int i = 0;i < right - left + 1;i++)
	{
		tmp[i] = array[left + i];
	}
	int i = 0;
	int j = midle-left+ 1;
	int k = 0;
	while (k < right - left + 1)
	{
		if (i > midle-left)
		{
			array[k + left] = tmp[j];
			j++;
		}
		else if (j > right-left)
		{
			array[k + left] = tmp[i];
			i++;
		}
		else if (tmp[i] < tmp[j])
		{
			array[k + left] = tmp[i];
			i++;
		}
		else
		{
			array[k + left] = tmp[j];
			j++;
		}
		k++;
	}
	delete[]tmp;
}

优化

优化1 当左边最后一个值大于右边第一个值才merge
优化2 组比较小的时候使用插入排序

template<typename T>
void __mergeSort(T array[],int left,int right )
{	
	if (right - left <= 15)//优化2 数组比较小的时候使用插入排序然后记得返回
	{
		insertSort<T>(array, left, right);
		return;
	}
	int midle = left + (right - left) / 2;//避免数太大越界
	__mergeSort(array, left, midle);
	__mergeSort(array, midle + 1, right);
	if(array[midle]>array[midle+1])//优化 当左边最后一个值大于右边第一个值才merge
		__merge(array, left, right, midle);
}

自底向上的方法(从小到大 不用递归)

template<typename T>
void mergeSortBU(T array[], int n)
{
	for (int sz = 1;sz < n;sz += sz)
	{
		for (int i = 0;i+sz < n;i += 2*sz)//i+sz是为了保证有第二部分 
		{
			__mergeSort(array, i, i + 2 * sz - 1<n-1? i + 2 * sz - 1:n-1);//三目运算符保证右边不越界
			
		}
	}
}

快速排序

原始方式

主函数

template <typename T>
void quickSort(T array[], int n)
{
	srand(time(NULL));
	__quickSort(array, 0, n - 1);
}

递归函数

template <typename T>
void __quickSort(T array[], int left, int right)
{
	if (left >= right)
	{
		return;
	}
	int p = __partition(array, left, right);
	    __quickSort(array, left, p - 1); 
		__quickSort(array, p + 1, right);
	
}

分类函数 partition

template <typename T>
int __partition(T array[], int left, int right)
{
	int low = left;//保证从[left+1,low]都是小于tmp的值
	T tmp = array[left];
	int high;//保证从[low+1,high)都大于tmp的值 high表示当前正在处理的值
	for(high=left+1;high<=right;high++)
		if (array[high] <= tmp)
		{
			sswap(&array[high], &array[++low]);//这里是++low
			//自带的swap函数运行很慢 所以自己写了swap函数速度正常
		}
		sswap(&array[left], &array[low]);//low成了
	return low;
}

partition优化(针对近乎有序序列)

随机选取p的值,通过生成随机种子,再将p的位置和最左边的位置交换,这样保证尽量左右两侧分布均匀,否则对于近乎有序的序列,每次分都是第一个值,两侧分布不均匀。

template <typename T>
int __partition(T array[], int left, int right)
{
	int a = rand() % (right - left + 1) + left;//优化 随机选取一个在左右两边的值
	sswap(&array[left],&array[a]);
	int low = left;//保证从[left+1,low]都是小于tmp的值

	T tmp = array[left];
	int high;//保证从[low+1,high)都大于tmp的值 high表示当前正在处理的值
	for(high=left+1;high<=right;high++)
		if (array[high] <= tmp)
		{
			sswap(&array[high], &array[++low]);//这里是++low
			//自带的swap函数运行很慢 所以自己写了swap函数速度正常
		}
		sswap(&array[left], &array[low]);
	return low;
}

优化快排1(针对大量重复序列)

主函数

template <typename T>
void __quickSort2(T array[], int left, int right)
{
	if (left >= right)
	{
		return;
	}
	int p = __partition2(array, left, right);
	__quickSort2(array, left, p - 1);

	__quickSort2(array, p + 1, right);
}
template <typename T>
void quickSort2(T array[], int n)
{
	srand(time(NULL));
	__quickSort2(array, 0, n - 1);
}

优化的partition函数

从两头开始搜索

template <typename T>
int __partition2(T array[], int left, int right)
{
	int a = rand() % (right - left + 1) + left;
	swap(array[left], array[a]);
	int low = left+1;//[left+1,low)都是小于p的数
	int high = right;//(high,right]都是大于p的数
	T tmp = array[left];

	while (true)
	{
		while (array[low] < tmp&&low<=right)low++;
		while (array[high] > tmp&&high>=left+1)high--;
		if (low > high)break;
		swap<T>(array[low], array[high]);
			low++;
			high--;
	}
	swap<T>(array[left], array[high]);
	return high;
}

快排优化3(针对大量重复)

主函数

template <typename T>
void quickSort3(T array[], int n)
{
	srand(time(NULL));
	__quickSort3(array, 0, n - 1);
}

优化partition函数

因为要返回两个指针值,所以不将partition写成函数了

template <typename T>
void __quickSort3(T array[], int left, int right)
{
	if (left >= right)
	{
		return;
	}

	//int midle = left;
	int a = rand() % (right - left + 1) + left;
	sswap(&array[left], &array[a]);
	T t = array[left];

	int low = left;//[left+1,low]是小于t的
	int high = right+1;//[high,right]是大于t的
	int tmp = left+1;//[low+1,tmp)是等于t的

	while (tmp < high)
	{
		if (array[tmp] > t)
		{
			sswap(&array[--high], &array[tmp]);
		}
		else if(array[tmp] < t)
		{
			sswap(&array[++low], &array[tmp++]);
		}
		else
		{
			tmp++;
		}
	}
	sswap(&array[left], & array[low]);

	__quickSort3(array, left, low-1);

	__quickSort3(array, high, right);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值