算法-数据结构之排序算法
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;
}
}
先构造堆,使每个父节点都比子节点大
随后将第一个元素与最后一个元素调换位置,将其下沉到合适位置,随后循环即可