六大排序算法的思想及C++代码实现


前言

这两天重新学习了一下六大排序算法,总结了一下发上来,仅供参考。


一、选择排序法 O(n^2)

1、思想

在第一轮遍历中,假设第一个值为最小值,然后在第二轮遍历中逐个与最小值比较,如果比当前最小值小就记录下它的index,第二轮遍历完后再用index与i比较,如果不相等再交换v[i]与v[index],以此从小到大排序。

2、代码

#include<iostream>
#include<vector>
using namespace std;

int main()
{
	vector<int> v = { 1,4,5,3,2,6,8 };

	int size = v.size();
	for (int i = 0; i < size - 1; i++)
	{
		int index = i;
		for (int j = i + 1; j < size; j++)
		{
			if (v[j] < v[index])
				index = j;
		}
		if (index != i)
		{
			int temp = v[i];
			v[i] = v[index];
			v[index] = temp;
		}
	}

	for (int j = 0; j < size; j++)
	{
		cout << v[j] << endl;
	}
}

二、插入排序法 O(n^2)

1.思想

取出某个值,与它之前的值进行比较,再在合适的地方插入。

2.流程

  1. 首先对数组的前两个数据进行从小到大的排序
  2. 接着将第三个数据与排好序的两个数据进行比较,将第三个数据插入到合适的位置。
  3. 接着将第四个数据与排好序的三个数据进行比较,将第四个数据插入到合适的位置。
  4. 不断重复上述流程,直到完成最后一个数据的插入。

3.代码

#include<iostream>
#include<vector>
using namespace std;

//降序
int main()
{
	vector<int> v = { 2,6,4,3,7,9,1,0 };
	int size = v.size();

	for (int i = 1; i < size; i++)
	{
		int temp = v[i];	//取出当前值
		int index = i;		//记录空着的位置

		for (int j = i - 1; j >= 0; --j)	//将当前值与之前的值进行比较
		{
			if (v[j] > temp)				//如果当前值小于之前的值
			{
				v[j + 1] = v[j];			//将之前的值右移
				index = j;					//记录空着的位置
			}
		}
		v[index] = temp;					//最后再将取出的当前值填入空着的位置
	}

	for (int i = 0; i < size; i++)
	{
		cout << v[i];
	}
	cout << endl;
}

三、冒泡排序法 O(n^2)

1、思想

通过每轮比较相邻元素使得数组逐渐趋于有序的过程。

2、代码

#include<iostream>
#include <vector>
using namespace std;
//降序
int main()
{
	vector<int> v  = { 2,4,6,3,1,5,7,9 };
	int size  = v.size();
	int temp;
	for (int i  = 0; i  < size  - 1; i++)          //比较的轮数 = size - 1
	{
		for (int j  = 0; j  < size  - i  - 1; j++)  //每轮需要比较的次数 = size - i - 1
		{
			if (v[j] > v[j  + 1])                //相邻元素进行比较,如果前面比后面大(降序),就交换
			{
				temp  = v[j];
				v[j] = v[j  + 1];
				v[j  + 1] = temp;
			}
		}
	}
	for (int i  = 0; i  < size; i++)
	{
		cout  << v[i];
	}
	cout  << endl;
}

四、冒泡排序法改进 O(n^2)

1、思想

引入target标志,目的是解决在所有轮跑完之前已经排序完毕,但是还要继续走完剩余轮数的情况。

2、代码

#include<iostream>
#include <vector>
using namespace std;
//降序
int main()
{
	vector<int> v = { 2,4,6,3,1,5,7,9 };
	int size = v.size();
	int target = 0;			//引入target,目的是为了解决在某轮排完后已经有序的数组还要进行无谓的比较的情况
							//target = 0表示数组已经排序完毕, target = 1表示数组还未排序完毕。
	int temp;

	for (int i = 0; i < size - 1; i++)			//比较的轮数 = size - 1
	{
		target = 0;								//先默认上一轮已经排完序了,这一轮还要再检验一遍是否真的已经排完序了
		for (int j = 0; j < size - i - 1; j++)	//每轮需要比较的次数 = size - i - 1
		{
			if (v[j] > v[j + 1])				//相邻元素进行比较,如果前面比后面大(降序),就交换
			{
				temp = v[j];
				v[j] = v[j + 1];
				v[j + 1] = temp;
				target = 1;						//当发生交换时,说明该轮无论是否排序完毕,都要下轮进行验证。
			}
		}
		if (target == 0)
			break;
	}

	for (int i = 0; i < size; i++)
	{
		cout << v[i];
	}
	cout << endl;
}

五、希尔排序 O(n^(1.3 ~2))

1、思想

将原先数组按组间距进行分组,然后对每个小组同时进行选择排序,之后再缩小组间距,直到组间距为1停止。

2、缺点:不稳定

3、代码

#include <iostream>
#include <vector>
using namespace std;
//降序
int main()
{
	vector<int> v = { 3,6,5,1,2,4,8,9,0 };
	int size = v.size();
	int gap = size;

	do
	{
		gap = gap / 3 + 1;			//对数组进行分组,gap为组间距

		for (int i = 0; i < size - 1; i += gap)
		{
			int index = i;
			for (int j = i + 1; j < size; j += gap)
			{
				if (v[j] < v[index])
					index = j;
			}

			if (index != i)
			{
				int temp = v[i];
				v[i] = v[index];
				v[index] = temp;
			}
		}
	} while (gap > 1);

	for (int i = 0; i < size; i++)
		cout << v[i];
	cout << endl;
}

六、快速排序法O(nlogn)

1、思想

选定数组的第一个值作为分隔值,将其它值与该值作比较,将小于该值的放在左边,大于该值的放在右边,分成左右两个子数组。再对左右两个子数组重复上述步骤,直到该子数组的长度为1为止。

2、缺点:不稳定

3、代码

#include<iostream>
#include<vector>

using namespace std;

//获取分隔值
int sort(vector<int> &v, int head, int tail)
{
	int temp = v[head];				//选定数组的第一个值作为分隔值
	int empty = 0;					//空位置标志位,0代表分隔值左边有空位,1代表分隔值右边有空位
	while (head != tail)			//循环结束的标志是头指针和尾指针指向同个位置
	{
		if (empty == 0)				//左边有空位的时候,就要从尾部找到比分隔值小的元素填充进去
		{
			if (v[tail] < temp)		//如果尾指针指向的值比分隔值小的话
			{
				v[head] = v[tail];	//将尾指针指向的值赋给头指针指向的空位置
				++head;				//再将头指针右移,尾指针保持不动记录着空位置
				empty = 1;			//empty置1表示右边有空位
			}
			else					//如果不比分隔值小的话就不用插入
			{
				--tail;				//直接左移尾指针即可
			}
		}
		else						//如果空位在右边的话,就要从头部找到比分隔值大的元素填充进去
		{
			if (v[head] > temp)		//如果头指针指向的值比分隔值大
			{
				v[tail] = v[head];	//将头指针指向的值赋给尾指针指向的空位置
				--tail;				//然后头指针保持不动记录空位置,尾指针左移
				empty = 0;			//empty置0表示左边有空位
			}
			else					//如果不比分隔值大的话就不用移动
			{
				++head;				//直接右移头指针即可
			}
		}
	}
	v[head] = temp;					//当头尾指针重合时,再把取出来的值放进去作为分隔点

	return head;					//返回分隔点的index
}

//将数组分割成左数组、分隔值、右数组
void split(vector<int> &v, int low, int high)
{
	if (low < high)		//终止条件,通过左右两个指针交叉来终止递归。
	{
		int split_index = sort(v, low, high);	//先对数组进行小、中、大三部分的排序

		split(v, low, split_index - 1);			//再对小数组重复上述步骤
		split(v, split_index + 1, high);		//再对大数组重复上述步骤
	}
}

int main()
{
	vector<int> v = { 3,5,6,8,4,1,2,9,0,7 };
	int size = v.size();

	split(v, 0, size - 1);

	for (int i = 0; i < size; ++i)
		cout << v[i];
	cout << endl;
}

七、归并排序法(nlogn)

1、思想

将数组递归分成两部分,每次依次取出两部分的一个元素进行比较,将较小的元素放入新数组中,直到所有元素都放完为止。

2、代码

#include<iostream>
#include<algorithm>
using namespace std;

//归并函数
void merge(int *data, int start, int end, int *result)
{
	int left_length = (end - start + 1) / 2 + 1;
	int left_index = start;						//左子数组起点
	int right_index = start + left_length;		//右子数组起点
	int result_index = start;
	while (left_index < start + left_length && right_index < end + 1)  //将左右两个数组的元素进行比较
	{
		if (data[left_index] <= data[right_index])			//如果左元素较小
			result[result_index++] = data[left_index++];	//就将左元素存入result,同时左指针右移
		else
			result[result_index++] = data[right_index++];	//否则就将右元素存入result,同时右指针左移
	}
	while (left_index < start + left_length)				//如果右指针到头了,但是左指针还没到头
		result[result_index++] = data[left_index++];		//直接把左指针剩下的部分全赋给result
	while (right_index < end + 1)							//如果左指针到头了,但是右指针还没到头
		result[result_index++] = data[right_index++];		//直接把右指针剩下的部分全赋给result
}

//分组函数
void merge_sort(int *data, int start, int end, int *result)
{
	if (1 == end - start)						//如果是只有两个元素,就比较大小后放回去
	{
		if (data[start] > data[end])
		{
			int temp = data[start];
			data[start] = data[end];
			data[end] = temp;
		}
		return;
	}
	else if (end == start)
		return;									//如果是只有一个元素,就直接放回去就好了
	else {
		int mid = (end - start + 1) / 2 + start;

		merge_sort(data, start, mid, result);	//分割左子数组
		merge_sort(data, mid + 1, end, result);	//分割右子数组

		//归并
		merge(data, start, end, result);
		for (int i = start; i <= end; ++i)
		{
			data[i] = result[i];
		}
	}

}

int main()
{
	int data[] = { 5,3,6,7,3,2,7,9,8,6,34,32,5,4,43,12,37 };
	int const length = sizeof(data) / sizeof(int);
	int *result = new int[length];

	cout << "before sorted:" << '\n';
	for (int i = 0; i < length; i++)
		cout << data[i] << ' ';
	cout << '\n'
		<< "after sorted:" << '\n';

	merge_sort(data, 0, length - 1, result);

	for (int i = 0; i < length; i++)
		cout << result[i] << ' ';

	delete[]result;
	result = NULL;

	return 0;
}

总结

排序算法应该是面试题出的比较多的了,不求全部记住,但是至少要记得一种O(n^2)和一种O(nlogn)的排序算法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值