排序算法

排序算法效率比较
排序方法平均时间复杂度最坏情况复杂度额外空间复杂度稳定性
基数排序O(D(N+R))O(D(N+R))O(N)稳定
归并排序O(N logN )O(N logN)O(N)稳定
冒泡排序O( N 2 N^2 N2O( N 2 N^2 N2)O(1)稳定
堆排序O(N logN)O(N logN)O(1)不稳定
快速排序O(N logN)O( N 2 N^2 N2)O(N logN)不稳定
  • 排序算法的稳定性是指:在一组待排序记录中,如果存在任意两个相等的记录R和S,且待排记录R在S前,如果存在排序后R依然在S前,即它们的前后位置在排序前后不发生变化,则该排序算法是稳定的。(参照:《数据结构》(陈越))

  • 基数排序的效率与基数的选择有关,其实际效果可以高于 O(N log N);
    快速排序具体到实际的平均时间效率上,是最优的;最坏情况下,不如堆排和归并排序;
    海量数据,用堆排。

1、基数排序

以 10 为基,次位优先的顺序依次调整数的顺序(个、十、百……),直到最大值所拥有的最高位。
每一趟中,按照当前的位,进桶,再统一收集……

#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

class Randix_sort{
	
	public:
		void sort(vector<int>&nums);
	
	private:
		int maxNum;
		vector<vector<int> >buckets;
	
}; 

void Randix_sort::sort(vector<int>&nums)
{
	if (nums.empty())
	{
		return ;
	}
	
	buckets.resize(10);
	maxNum = *max_element(nums.begin(),nums.end());
	
	int i = 1;
	
	while(maxNum)
	{
		for (int j = 0;j<nums.size();j++)
		{
			if (nums[j]<0)
			{
				cout<<"can't sort the negative value"<<endl;
				return;
			}
			
			int tmp = (nums[j]/i) % 10;
			buckets[tmp].push_back(nums[j]);
		}
		
		nums.clear();
		
		for (int k = 0;k<buckets.size();k++)
		{
			for (int kk=0;kk<buckets[k].size();kk++)
			{
				nums.push_back(buckets[k][kk]);
			}
		}
		
		for (int j=0;j<10;j++)
		{
			buckets[j].clear();
		}
		
		maxNum /= 10;
		i*=10;
		
	}
	
}


int main()
{
	vector<int>input{10,3,12,2,34};
	
	Randix_sort r;
	r.sort(input) ;
	
	for (auto o : input)
	{
		cout<<o<<endl;
	}
	
	return 0;
}
2、快速排序

主元pivot的选择会影响效率。
这里将 A[Low]、A[High]、A[(low+high)/2]三者关键字的中值作为pivot,并调整顺序为“最左元<=主元<=最右元”,将主元放在倒数第二的位置上(因为最右元大于主元)

#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;


void Quick_Sort(vector<int>&v, int left, int right)
{

	if (left < right)
	{
		// 调整主元顺序

		int mid = (left + right) / 2;
		if (v[left]>v[right])swap(v[left], v[right]);
		if (v[left]>v[mid])swap(v[left], v[mid]);
		if (v[mid]>v[right])swap(v[mid], v[right]);

		int pivot = v[mid];
		swap(v[mid], v[right - 1]);    // 将主元放在倒数第二位

		int i = left, j = right - 1;

		while (1) {    // 每一趟 的调整

			if (i == right || j == left)break;

			while (v[++i]<pivot);
			while (v[--j]>pivot);

			if (i<j)swap(v[i], v[j]);
			else break;
		}

		swap(v[right - 1], v[i]);     // 左边小于主元,右边大于主元;

		Quick_Sort(v, left, i - 1);
		Quick_Sort(v, i + 1, right);

	}
}

int main()
{
	vector<int>v = { 1,4,2,99,5,724,33 };

	Quick_Sort(v, 0, v.size() - 1);

	for (int i = 0; i<v.size(); i++)
	{
		cout << v[i] << " ";
	}
	getchar();
	return 0;

}

如果是从大到小排序,注意,大于小于号的顺序,另外,特别注意,mid = (left+right+1)/2,每次交换的是主元和 r

void Quick_Sort(vector<int>&v, int left, int right)
{

	if (left < right)
	{
		// 调整主元顺序
            int mid = (left+right+1)/2;                //!!!!!  
            // int mid;
            // if (((left+right)%2)==0)
            
            if (v[left]<v[right])swap(v[left],v[right]);
            if (v[left]<v[mid])swap(v[left],v[mid]);
            if (v[mid]<v[right])swap(v[mid],v[right]);
            
            int pivot = v[mid];
            // cout<<"pivot: "<<pivot<<" ";
            swap(v[mid],v[left+1]);
            
            int l = left+1;
            //int l = left;
            int r = right;
            while(1)
            {
                if (l==right || r==left)break;
                
                while( v[++l]>pivot);
                while( v[--r]<pivot);
                
                if (l<r)swap(v[l],v[r]);
                else 
            {   
                // cout<<"yse"<<" ";
                // cout<<" L: "<<l<<" R: "<<r<<" ";
                break;
            }
            }
            
            swap(v[r],v[left+1]);
            //swap(v[r],v[mid]);

        // cout<<"left: "<<left<<" right: "<<right<<" v[mid] "<<v[mid]<<" r: "<<r<<endl;
        // for(int i=0;i<v.size();i++)cout<<v[i]<<" ";
        // cout<<endl;

		Quick_Sort(v, left, r - 1);
		Quick_Sort(v, r + 1, right);

	}
}

topK,找第K大的数,也可以用快排做,每次返回的是 中轴位置,根据位置,判读向左半边还是右半边找第K大。
时间复杂度是 O(N)??http://haoyuanliu.github.io/2016/12/18/Partition算法剖析/
题目:
https://leetcode.com/problems/kth-largest-element-in-an-array/submissions/

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        
        
        int left = 0;
        int right = nums.size()-1;
        
        while (1)
        {
            int t = Partition(nums,left,right);
            
            if (t == k-1)return nums[t];
            else if (t>k-1)right = t-1;
            else if (t<k-1)left = t+1;
        }
        
    }
    
    int Partition(vector<int>&v,int left,int right)
    {
        //if (left<right){
        
            if (left==right)return left;
        
            int mid = (left+right+1)/2;
            
            if (v[left]<v[right])swap(v[left],v[right]);
            if (v[left]<v[mid])swap(v[left],v[mid]);
            if (v[mid]<v[right])swap(v[mid],v[right]);
            
            int pivot = v[mid];
            swap(v[mid],v[left+1]);
            
            int l = left+1;
            int r = right;
            while(l<=r)
            {
                if (l==right || r==left)break;
                
                while(v[++l]>pivot);
                while(v[--r]<pivot);
                
                if (l<r)swap(v[l],v[r]);
                else break;
            }
            
            swap(v[r],v[left+1]);
            return r;
       // }
       // else 
           // return left;
        // return r;
    }
    
};
3、归并排序(递归与非递归)

递归算法从上往下考虑,假设左半部分和右半部分已经排序好,需要将这两部分merge ;
非递归算分从下往上考虑,从size = 1开始,size依次乘以2

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

void merge(vector<int>&tmp, vector<int>&v, int left, int right)
{

	int mid = (left + right) / 2;

	int k = left;
	int r = mid + 1;

	int i = k;

	for (; k <= mid & r <= right;)
	{
		if (v[k]<v[r])
		{
			tmp[i++] = v[k++];
		}
		else
		{
			tmp[i++] = v[r++];
		}
	}

	while (k <= mid)tmp[i++] = v[k++];
	while (r <= right)tmp[i++] = v[r++];

	for (int j = left; j <= right; j++)
	{
		v[j] = tmp[j];
	}

	//copy(v.begin() + left, v.begin() + right, back_inserter(tmp));

}


void merge_sort(vector<int>&tmp, vector<int>&v, int left, int right)
{

	if (left <right)
	{
		int mid = (left + right) / 2;
		merge_sort(tmp, v, left, mid);
		merge_sort(tmp, v, mid + 1, right);
		merge(tmp, v, left, right);
	}

}

// 非递归算法
void merge_sor_no_recursion(vector<int>&v)
{
	int n = v.size();
	int l1, l2, h1, h2, k;
	vector<int>tmp(n);

	int size = 1;
	for (; size<n; size *= 2)  // 每一趟 归并
	{

		l1 = 0;  //左部分的最小下标
		k = 0;
		while (l1 + size<n)
		{
			h1 = l1 + size - 1;  // 左部分的最高下标
			l2 = h1 + 1;          // 右部分的最小下标
			h2 = l2 + size - 1;   // 右部分的最大下标

			if (h2 >= n)h2 = n - 1;

			int i = l1;
			int j = l2;
			while (i <= h1 && j <= h2)
			{
				if (v[i]<v[j])
				{
					tmp[k++] = v[i++];
				}
				else
				{
					tmp[k++] = v[j++];
				}
			}

			while (i <= h1)tmp[k++] = v[i++];
			while (j <= h2)tmp[k++] = v[j++];
			l1 = h2 + 1;
		}

		while (k<n)tmp[k++] = v[l1++];

		for (int i = 0; i<n; i++)v[i] = tmp[i];   // 一趟结束

		printf("\nSize=%d \nElements are : ", size);
		for (int i = 0; i<n; i++)
			printf("%d ", v[i]);

	}

}



int main()
{
	vector<int>v = { 2,33,3,3,1,88,4,56,20 };
	int n = v.size();
	vector<int>tmp(n);

	//	merge_sort(tmp, v, 0, n - 1);
	merge_sor_no_recursion(v);
	cout << endl;
	for (int i = 0; i<n; i++)
	{
		cout << v[i] << " ";
	}

	getchar();

	return 0;
}

// reference: https://www.includehelp.com/ds/merge-sort-with-and-without-recursion-using-c-program.aspx
4、堆排序

如果要从小到大排,则建立一个最大堆,每次把最大堆堆顶的元素移到末尾,堆的数量减1,因此,不用额外的空间。
建堆和调整的核心代码一致。
下标[N/2 -1] 是最后一个有叶节点的节点。

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

void PercDown(vector<int>&v, int N, int p)
{

	int parent = p;
	int tmp = v[parent];
	int child;
	for (; (parent * 2 + 1)<N; parent = child) {
		child = 2 * parent + 1;
		if ((child + 1) < N && v[child]<v[child + 1])
		{
			child = child + 1;
		}

		if (tmp >= v[child])
		{
			break;
		}

		else {
			v[parent] = v[child];
		}

	}

	v[parent] = tmp;

}

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

	int N = v.size();

	for (int k = N / 2 - 1; k >= 0; k--)
	{
		PercDown(v, N, k);
	}

	for (int i = N - 1; i >= 0; i--)
	{
		swap(v[i], v[0]);
		PercDown(v, i, 0);
	}

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

	getchar();
	return 0;
}
5、冒泡排序

冒泡排序要排 (N-1)趟,每一趟要把剩余要排序列的最大值排在最后。

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

int main()
{
	vector<int>v = { 2,1,88,4,9,5 };
	int N = v.size();

	bool flag;

	for (int i = 0; i<N - 1; i++)  // 每次把最大的移到最后
	{
		flag = false;

		for (int j = 0; j <N - i -1; j++)  // 只对前 N - i 个进行比较
		{
			if (v[j]>v[j + 1])
			{
				swap(v[j], v[j + 1]);
				flag = true;
			}
		}

		if (flag == false)break;
	}

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

	getchar();

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值