算法-数据结构之排序算法

1.简单排序方法

1.1选择排序

class Solution {
public:
    void sortIntegers(vector<int>& A) {
       int len = A.size();
       for(int a = 0;a<len;a++)
       {
           for(int i = a+1;i<len;i++)
           if(A[a]>A[i]) swap(A[a],A[i]);
       }
      
    }
};

1.2.插入排序

class Solution {
public:
    void sortIntegers(vector<int>& A) {
       int len = A.size();
       for(int a = 1;a<len;a++)
       {
           for(int j = a;j>0&&A[j]<A[j-1];j--)
           {
               swap(A[j],A[j-1]);
           }
       }
      
    }
};

1.3.希尔排序

class Solution {
public:
    void sortIntegers(vector<int>& A) {
       int len = A.size();
       int h = 1;
       while(h<len/3) h = 3 * h +1;
       while(h>=1)
       {
           for(int i = h;i<len;i++)
           {
               for(int j = i;j>=h && A[j]<A[j-h];j-=h)  swap(A[j],A[j-h]);
           }
           h/=3;
       }
       
    }
};

本质上是先分段将h增量上的数组变得有序,直到h=1时数组基本有序,此时便是插入排序,但却基本有序,所以效率高。

2.归并排序

2.1原地归并的抽象方法

template<typename T>
void merge(vector<T>&nums, int lo, int hi, int mid)
{
	int i = lo, j = mid + 1;
	int *temp = new int[hi + 1];
	for (int k = lo; k <= hi; k++)
		temp[k] = nums[k];
	for (int k = lo; k <= hi; k++)
	{
		if (i > mid)
			nums[k] = temp[j++];
		else if (j > hi)
			nums[k] = temp[i++];
		else if (temp[i] < temp[j])
			nums[k] = temp[i++];
		else
			nums[k] = temp[j++];
	}
	delete [] temp;
}

2.2自顶向下的归并排序

template<typename T>
void sort(vector<T>&nums, int lo, int hi)
{
	if (hi <= lo)
		return;
	int mid = (lo + hi) / 2;
	sort(nums, lo, mid);
	sort(nums, mid + 1, hi);
        merge(nums, lo, hi, mid);
}

2.3对自顶向下的归并排序的优化

2.3.1 对已经有序的数组不需要进行归并

template<typename T>
void sort(vector<T>&nums, int lo, int hi)
{
	if (hi <= lo)
		return;
	int mid = (lo + hi) / 2;
	sort(nums, lo, mid);
	sort(nums, mid + 1, hi);
	if(nums[mid]>nums[mid+1])  //加上这一个if语句
		merge(nums, lo, hi, mid);
}

2.3.2插入排序代替归并排序

template<typename T>
void sort(vector<T>&nums, int lo, int hi)
{
	if (hi < 16)  //当hi小于16时用插入排序代替归并排序
	{
		InsertSort(nums, lo, hi);
		return;
	}
	if (hi <= lo)
		return;
	int mid = (lo + hi) / 2;
	sort(nums, lo, mid);
	sort(nums, mid + 1, hi);
	if(nums[mid]>nums[mid+1])
		merge(nums, lo, hi, mid);
}
template<typename T>
void InsertSort(vector<T>& nums, int lo, int hi)
{
	for (int i = lo; i <= hi; i++)
		for (int j = i; j > lo&&nums[j] < nums[j - 1]; j--)
			swap(nums[j], nums[j - 1]);
}

2.3.3使用常量辅助空间进行归并排序

全局声明辅助数组

int*temp;

Sort方法的实现

void Sort(vector<T>&nums)
{
	temp = new int[nums.size()];
	sort(nums, 0, nums.size() - 1);
}

2.3.4输入数组与辅助数组的交换

由于代码实现困难,vector申请太多空间后会异常,普通数组又无法实现长度的自我计算

但也是可以实现的,只需将sort增加辅助数组,随后递归中交换两者角色即可

2.4自底向上的归并排序

template<typename T>
void _Sort(vector<T>&nums)
{
	int len = nums.size();
	temp = new int[len];
	for (int sz = 1; sz < len;sz=sz+sz)
		for (int lo = 0; lo < len - sz; lo += sz + sz)
			merge(nums, lo, lo + sz + sz - 1 > len-1 ? len-1 : lo + sz + sz - 1, lo + sz - 1);
}

经过测试,在50000元素数组中,自底向上的排序方法优先

或许是因为我没有使用辅助数组与输入数组交换的优化方法,但两者相差不大。

3.快速排序

3.1.切分方法

template<typename T>
int partition(vector<T>&nums, int lo, int hi)
{
	int i = lo, j = hi + 1;
	T v = nums[lo];
	while (true)
	{
		while (nums[++i] < v)
			if (i == hi)
				break;
		while (v < nums[--j])
			if (j == lo)
				break;
		if (i >= j)
			break;
		swap(nums[i], nums[j]);
	}
	swap(nums[lo], nums[j]);
	return j;
}

3.2快速排序源代码

template<typename T>
void QuickSort(vector<T>&nums, int lo, int hi)
{
	if (hi <= lo+5 )return;
	int j = partition(nums, lo,  hi);
	QuickSort(nums, lo, j-1);
	QuickSort(nums, j + 1, hi);
}

3.3快速排序优化之一:切换至插入排序

if (hi <= lo+5 )
	{
		InsertSort(nums, lo, hi);
		return;
	}

效率可提高不少

3.4三向切分的快速排序

template<typename T>
vid QuickSort3Way(vector<T>&nums, int lo, int hi)
{
	if (hi <= lo)
		return;
	int lt = lo, i = lo + 1, gt = hi;
	T v = nums[lo];
	while (i <= gt)
	{
		if (nums[i] < v)
			swap(nums[lt++], nums[i++]);
		else if (nums[i] > v)
			swap(nums[i], nums[gt--]);
		else i++;
	}
	QuickSort3Way(nums, lo, lt - 1);
	QuickSort3Way(nums, gt + 1, hi);
}

4.优先队列的堆排序

堆本质即是二叉树,而完全二叉树(父节点比子节点大)可以用数组表示

假如有N个元素,那么需要N+1的空间,因为第一个元素是不使用的

一个结点的位置若是k,那么他的父节点即是k/2,他的两个子节点分别是2k与2k+1

有了这些知识,我们就可以进行堆排序了

4.1基本组成

template<typename T>
class MaxPQ
{
private:
	T*pq;
	int N = 0;
	bool Less(int i, int j)
	{
		return pq[i] < pq[j];
	}
	void Exchange(int i, int j)
	{
		T temp = pq[i];
		pq[i] = pq[j];
		pq[j] = temp;
	}
	void Swim(int k){}
	void Sink(int k){}
public:
	MaxPQ(int maxN);
	bool IsEmpty();
	int Size();
	void Insert(T v);
	T DelMax();
}; 

4.2简单方法的实现

template<typename T>
inline MaxPQ<T>::MaxPQ(int maxN)
{
	pq.=new T [maxN+1];
}


template<typename T>
bool MaxPQ<T>::IsEmpty()
{
	return N == 0;
}


template<typename T>
int MaxPQ<T>::Size()
{
	return N;
}

4.3插入元素与上浮

我们插入元素,先将元素放至底部,随后采用上浮方式将其放置于合适位置

插入方法的实现:

template<typename T>
void MaxPQ<T>::Insert(T v)
{
	pq[++N] = v;
	swim(N);
}

上浮swim方法的实现

void Swim(int k)
{
	while (k > 1 && less(k/2, k))
	{
		Exchange(k / 2, k);
		k /= 2;
	}	
}

4.4删除元素与下沉

删除元素,将最大元素与最后一个元素位置调换,随后,删除,将第一个元素下沉至合适位置

template<typename T>
T MaxPQ<T>::DelMax()
{
	T max = pq[1];
	Exchange(1, N--);
	pq[N + 1] = nullptr;
	sink(1);
	return max;
}

下沉方法的实现

void Sink(int k)
{
	while (2 * k <= N)
	{
		int j = 2 * k;
		if (j < N&&less(j, j + 1))
			j++;
		if (less(j, k))
			break;
		Exchange(j, k);
		k = j;
	}
}

4.5调整动态数组的大小

首先实现一个resize方法

template<typename T>
void MaxPQ<T>::resize(int max)
{
	T * temp = new T[max];
	for (int i = 1; i <= N; i++)
		temp[i] = pq[i];
	delete[] pq;
	pq = temp;
}

随后在插入方法中实现

template<typename T>
void MaxPQ<T>::Insert(T v)
{
	if (N + 1 == length)
		resize(2 * length);
	pq[++N] = v;
	swim(N);
}

在删除方法中实现

template<typename T>
T MaxPQ<T>::DelMax()
{
	
	T max = pq[1];
	Exchange(1, N--);
	if (N > 0 && N < length / 4)
		resize(length / 2);
	sink(1);
	return max;
}

4.6完整代码

#include<vector>
using namespace std;
template<typename T>
class MaxPQ
{
private:
	int*pq;
	int N = 0;
	int length;
	bool Less(int i, int j)
	{
		return pq[i] < pq[j];
	}
	void Exchange(int i, int j)
	{
		T temp = pq[i];
		pq[i] = pq[j];
		pq[j] = temp;
	}
	void Swim(int k)
	{
		while (k > 1 && less(k/2, k))
		{
			Exchange(k / 2, k);
			k /= 2;
		}
	}
	void Sink(int k)
	{
		while (2 * k <= N)
		{
			int j = 2 * k;
			if (j < N&&less(j, j + 1))
				j++;
			if (less(j, k))
				break;
			Exchange(j, k);
			k = j;
		}
	}
	void resize(int max);
public:
	MaxPQ(int maxN);
	~MaxPQ();
	bool IsEmpty();
	int Size();
	void Insert(T v);
	T DelMax();
}; 
#include"堆排序.h"

template<typename T>
inline MaxPQ<T>::MaxPQ(int maxN)
{
	pq.resize(maxN + 1);
	length = maxN + 1;
}
template<typename T>
MaxPQ<T>::~MaxPQ()
{
	delete[] pq;
}

template<typename T>
bool MaxPQ<T>::IsEmpty()
{
	return N == 0;
}


template<typename T>
int MaxPQ<T>::Size()
{
	return N;
}


template<typename T>
void MaxPQ<T>::Insert(T v)
{
	if (N + 1 == length)
		resize(2 * length);
	pq[++N] = v;
	swim(N);
}

template<typename T>
T MaxPQ<T>::DelMax()
{
	
	T max = pq[1];
	Exchange(1, N--);
	if (N > 0 && N < length / 4)
		resize(length / 2);
	sink(1);
	return max;
}

template<typename T>
void MaxPQ<T>::resize(int max)
{
	T * temp = new T[max];
	for (int i = 1; i <= N; i++)
		temp[i] = pq[i];
	delete[] pq;
	pq = temp;
	length = max;
}

5.堆排序的实现

template<typename T>
void DuiSort(vector<T>& nums)
{
	int len = nums.size()-1;
	for (int k = len / 2; k >= 1; k--)
		sink(nums, k, len);
	while (len > 1)
	{
		swap(nums[1], nums[len--]);
		sink(nums, 1, len);
	}
}
template<typename T>
void sink(vector<T>& nums, int k, int len)
{
	while (2 * k <= len)
	{
		int j = 2 * k;
		if (j < len&&nums[j]<nums[j+1])
			j++;
		if (nums[k]>nums[j])
			break;
		swap(nums[k], nums[j]);
		k = j;
	}
}

先构造堆,使每个父节点都比子节点大

随后将第一个元素与最后一个元素调换位置,将其下沉到合适位置,随后循环即可

posted @ 2017-07-30 23:52  vhyz  阅读(195)  评论(0编辑  收藏  举报