剑指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;
}

      

猜你喜欢

转载自blog.csdn.net/super_gk/article/details/80642408