剑指offer学习笔记——排序1:整体总结与C++实现

排序算法是必须要掌握的基础知识,针对于各种排序算法,下面是基础的C++实现:

 

排序方法平均情况最好情况最坏情况辅助空间稳定性
冒泡排序O(n^2)O(n)O(n^2)O(1)稳定
简单选择排序(键值交换为O(n))O(n^2)O(n^2)O(n^2)O(1)不稳定
直接插入排序(基本有序时表现最好)O(n^2)O(n)(升序)O(n^2)(降序)O(1)稳定
希尔排序(插入排序的增强版)O(nlogn)~O(n^2)O(n^1.3)O(n^2)O(1)不稳定
堆排序O(nlogn)O(nlogn)O(nlogn)O(1)不稳定
归并排序O(nlogn)O(nlogn)O(nlogn)O(n)(缺点,需额外空间)稳定
快速排序O(nlogn)O(nlogn)O(n^2)O(logn)~O(n)(因为迭代)不稳定

 

1、选择排序:

 

选择排序的优势在于键值交换仅为O(n)

void SelectionSort(int (&A)[N])      
{
	for (int i = 0; i < N - 1; i++)   //从0开始,共操作到N-2下标,操作N-1次。即可完成排序
	{
		int min = i;
		for (int j = i+1; j < N; j++)
			if (A[j] < A[min])
				min = j;
		swap(A[min], A[i]);
	}
	cout << "Selection Sort:\n"<<"Time: Avg-N2  不稳定\n";
	for (auto &a : A)
		cout << a << " ";
	cout << endl << endl << endl;
}

2、冒泡排序

稳定是其优点!!

void BubbleSort(int(&A)[N])
{
	for (int i = 0; i < N - 1; i++)   //记录次数 共需操作N-1次
		for (int j = 0; j < N - 1 - i; j++)
			if (A[j]>A[j + 1])
				swap(A[j], A[j + 1]);
	cout << "Bubble Sort:\n" << "Time: Avg-N2  Best-N  稳定\n";
	for (auto &a : A)
		cout << a << " ";
	cout << endl << endl << endl;
}

3、插入排序

插入排序在遇到基本有序的数组时表现是最好的,而且是稳定的

void InsertSort(int(&A)[N])
{
	for (int i = 0; i < N; i++)
	{
		int val = A[i];
		int j = i;      //从后往前进行寻找
		while (j > 0 && A[j - 1] > val)
		{
			A[j] = A[j - 1];
			j--;
		}
		A[j] = val;  //找到合适位置之后插入
	}
	cout << "Insert Sort:\n" << "Time: Avg-N2  Best-N 稳定\n"<<"注意:插入排序对于基本有序的序列表现很好,接近N"<<endl;
	for (auto &a : A)
		cout << a << " ";
	cout << endl << endl << endl;
}

4、合并排序

优点是稳定,缺点是需要额外空间

void Merge(int start, int end, int(&A)[N], int(&res)[N])
{
	int left = (end - start + 1) / 2;
	int left_ind = start;
	int right_ind = start + left;
	int res_ind = start;
	while (left_ind < start + left && right_ind<=end)
	{
		if (A[left_ind] <= A[right_ind])
			res[res_ind++] = A[left_ind++];
		else
			res[res_ind++] = A[right_ind++];
	}
	while (left_ind < start + left)
		res[res_ind++] = A[left_ind++];
	while (right_ind <= end)
		res[res_ind++] = A[right_ind++];
}


void MergeSort(int start, int end, int(&A)[N], int(&res)[N])
{
	if (end - start == 1&&A[start]>A[end])
	{
		swap(A[end], A[start]);
		return;
	}
	else if (end == start)
		return;
	else
	{
		MergeSort(start, (end - start + 1) / 2 + start - 1, A, res);
		MergeSort((end - start + 1) / 2 + start, end, A, res);
		Merge(start, end, A, res);
		for (int i = start; i <= end; i++)   //元素复制,必不可少的一步
			A[i] = res[i];
	}
}

5、快速排序

表现较好,但是遇到有序序列排序的时候会退化为冒泡

int Partition(int low, int high, int(&A)[N])
{
	int key = A[low];
	while (high > low)
	{
		while (high > low&&A[high] >= key)	high--;
		swap(A[high], A[low]);
		while (high > low&&A[low] <= key)	low++;
		swap(A[low], A[high]);
	}
	return low;
}

void QuickSort(int low, int high, int(&A)[N])
{
	if (high > low)
	{
		int p = Partition(low, high, A);
		QuickSort(low, p - 1, A);
		QuickSort(p + 1, high, A);
	}
}

6、堆排序:构造堆——>堆排序

在堆中删除根键是:O(logN)

在叶子上插入键也是:O(logN)

堆排是:构造堆O(N)+n-1次的删除操作O(NlogN),所以最终时间复杂度为O(NlogN)

void HeapBottomTop(int(&A)[N+1])  //自底向上构造堆
{
	for (int i = N / 2; i > 0; i--)
	{
		int k = i, j = 2 * k;
		while (j <= N )
		{
			if (j < N  && A[j + 1] > A[j])
				j = j + 1;
			if (A[j] > A[k])
			{
				swap(A[k], A[j]);
				k = j;
				j = 2 * k;
			}
			else
				break;
		}
	}
}

void makeHeap(int end, int(&A)[N + 1])
{
	int k = 1, j = 2 * k;
	while (j <= end)
	{
		if (j < end  && A[j + 1] > A[j])
			j = j + 1;
		if (A[j] > A[k])
		{
			swap(A[k], A[j]);
			k = j;
			j = 2 * k;
		}
		else
			break;
	}
}

void HeapSort(int(&A)[N + 1])
{
	HeapBottomTop(A);
	for (int i = 1; i < N ; i++)
	{
		swap(A[1], A[N + 1 - i]);   //相当于删除最大的键
		makeHeap(N - i, A);         //重新堆化
	}
	cout << "Heap Sort:\n" << "Time: Avg-NlogN    不稳定" << endl;
	for (int i = 1; i < N + 1;i++)
		cout << A[i] << " ";
	cout << endl << endl << endl;
}

7.拓扑排序

用于课表选择等等,感觉上是一个O(N2)

//拓扑排序
//1. Kahn算法,BFS统计入度vector<vector<int>> edges为边连通的情况;
void TopologicalSort()
{
	int N;//顶点数
	vector<vector<int>> edges;
	vector<int> res;
	vector<int> indegree(N, 0);
	for (int i = 0; i < edges.size(); i++)
		for (int j = 0; j < edges[i].size(); j++)
			indegree[j]++;
	queue<int> q;
	for (int i = 0; i < N; i++)
		if (indegree[i] == 0)
			q.push(i);
	int cnt = 0;
	while (!q.empty())
	{
		int tmp = q.front();
		q.pop();
		cnt++;
		res.push_back(tmp);
		for (int i = 0; i < edges[tmp].size(); i++)
		{
			int tmpnode = edges[tmp][i];
			if (--indegree[tmpnode] == 0)
				q.push(tmpnode);
		}
	}
	if (cnt == N)
		cout << "可以进行拓扑排序,结果存储在res" << endl;
	else
		cout << "无法进行拓扑排序,存在环" << endl;
}

//2.使用DFS的算法
void TopologicalSort()
{
	vector<vector<int>> edges;
	vector<int> res;
	vector<int> visit(N, 0);
	for (int i = 0; i < N;i++)
		if (!DFS(edges, visit, res, i))
			res.clear();                      //无法进行拓扑排序
	if (res.size()==N)   //如果可以进行拓扑排序
		reverse(res.begin(), res.end());          //然后去反得到拓扑序
}

bool DFS(vector<vector<int>> &edges, vector<int> &visit, vector<int> &res, int node)
{
	if (visit[node] == -1)
		return false;  //说明成环,无法进行拓扑排序
	else if (visit[node] == 1)
		return true;
	visit[node] = -1;
	for (int i = 0; i < edges[node].size(); i++)
		if (!DFS(edges, visit, res, edges[node][i]))
			return false;
	visit[node] = 1;
	res.push_back(node);   //记录其出栈的顺序(关键)!!!
	return true;
}

 

      

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值