算法学习小心得——基于比较的排序算法汇总

这几天学习了基于比较的排序算法,有插入排序、归并排序、堆排序和快速排序。下面自己用C++代码实现了一下:

1、插入排序,插入排序的思想比较简单,就是像我们打扑克牌把手中的牌整理好一样,假设手中的牌是已经排好的,只需要找找到适当的位置把下一张牌插入到已有的牌中就好了。

void InsertionSort(int *A,int beg,int end){
	int key;
	for(int i=beg+1;i<=end;i++){//对所有的牌进行遍历,假设手中只有一张牌。
		key=A[i];//用key来表示要插入的牌
		int j=i-1;
		while(j>=0 && A[j]>key){//找到正确的位置:如果手中的牌比要插入的大就往后移,直到正确的位置
			A[j+1]=A[j];
			j--;
		}
		A[j+1]=key;//把要插入的牌放到正确的位置
	}
}

2、归并排序,归并排序使用了分治的方法,即把问题分为若干个子问题再分别解决每个子问题。主要思想是这样的,如果a和b是两个已经排好序的数组,那么可以比较a和b中的元素大小,把他们合并成一个排好序的数组。所以就把整个要排序的数组进行划分,直到每个字数组中只有一个元素(一个元素肯定是排好序的)。然后开始合并,直到最后合并成原长度的数组。这里用了一个Merge作为合并函数。

void Merge(int *A,int beg,int mid,int end){//合并函数
	int n1=mid-beg+1,n2=end-mid;
	int i1=beg,i2=mid+1;
	vector<int> v;//用了一个vector来做中间数组,这里我纯粹是为了练习,完全可以用一个数组
	while(i1<=mid && i2<=end){
		if(A[i1]<A[i2]){
			v.push_back(A[i1++]);
		}
		else
			v.push_back(A[i2++]);
	}
	while(i1<=mid){
		v.push_back(A[i1++]);
	}
	while(i2<=end){
		v.push_back(A[i2++]);
	}
	int n=beg;
	auto it=v.begin();
	while(it!=v.end()){//将合并好的数组拷入到原数组中
		A[n++]=*it;
		it++;
	}
}
void MergeSort(int *A,int beg,int end){//进行归并
	if(beg<end){//如果数组元素不为1,就进行划分
		int mid=beg+(end-beg)/2;
		MergeSort(A,beg,mid);
		MergeSort(A,mid+1,end);
		Merge(A,beg,mid,end);
	}
}

3、堆排序,堆排序是使用了最大堆这种数据结构。最大堆就是堆顶的元素是堆中最大的。堆排序的主要思想就是用数组来构造最大堆。这样每次把堆顶元素和数组中最后一个元素调换位置,直到堆中只有一个元素,这样数组的元素就从大到小了。这个算法写了一个堆的类,用成员函数进行排序。

class Maxheap{
private:
	int size;//存放堆得大小
	int length;//存放数组的长度
	vector<int> data;//存放堆中元素
public:
	Maxheap(){size=0;length=0;}
	void Add(int value){
		data.push_back(value);
		size++;
		length++;
	}
	void MaxHeapify(int i);
	void BuildMaxheap();
	void HeapSort();
	int Get(int i){if(i<length)return data[i];else cout<<"overflow";}
	int Getlen(){return length;}
};

void Maxheap::MaxHeapify(int i){//这个函数用来不断的维护堆的性质,因为建堆的时候需要用到,而且调换了堆顶元素和堆中最后一个元素后会破坏堆的性质。
	int l=2*i+1,r=2*i+2;//找到左孩子和又孩子的序号
	int largest=i,temp;
	if(l<size && data[l]>data[i]){
		largest=l;
	}
	if(r<size && data[r]>data[largest]){
		largest=r;
	}//将根、左孩子、右孩子中最大的作为根
	if(largest!=i){//如果左子树和右子树的根变化,可能破坏了子树的性质,所以再次对子树进行维护。
		temp=data[largest];
		data[largest]=data[i];
		data[i]=temp;
		MaxHeapify(largest);
	}
}

void Maxheap::BuildMaxheap(){//建堆
	size=length;
	for(int i=length/2-1;i>=0;i--){//因为对树来说,后n/2节点为叶节点,所以不需要维护,只维护前n/2
		MaxHeapify(i);
	}
}

void Maxheap::HeapSort(){//排序
	BuildMaxheap();//先建堆
	int temp;
	while(size>1){//然后不断的把堆顶元素移到堆尾,并且维护堆的性质
		temp=data[size-1];
		data[size-1]=data[0];
		data[0]=temp;
		size--;
		MaxHeapify(0);
	}
}
4、快速排序,这个排序相对比较难懂,也是用了分治的策略。把目标数组的最后一个元素作为主元素,然后对前n-1个进行遍历,试数组被分为起点到i,i到j,j到末尾3段,第一段的数都小于主元素,第二段的数都大于主元素,第三段没有排,然后知道第三段的元素都被移除到第一段和第二段。再把主元素和到i+1的位置(大于主元素的位置)的元素调换,这样就被分成了两段,前一段都小于主元素,后一段都大于主元素。再对这两段递归的执行相同算法。
int Partition(int *A,int beg,int end){//用于划分
	int key=A[end];
	int i=beg-1,j=beg;
	int temp;
	while(j<end){
		if(A[j]<key){
			temp=A[++i];
			A[i]=A[j];
			A[j++]=temp;
		}
		else
			j++;
	}
	temp=A[++i];
	A[i]=A[end];
	A[end]=temp;
	return i;//返回后i之前的元素都小于A[i],i之后的都大于A[i]
}

void QuickSort(int *A,int beg,int end){
	if(beg<end){
		int p=Partition(A,beg,end);
		QuickSort(A,beg,p-1);
		QuickSort(A,p+1,end);
	}
}

除了插入排序的时间复杂度是N平方外,其他都是nlgn,对于快速排序来说这个时间复杂度是平均的,而不是最坏的,当然如果随机的来选取主元素的话,是可以打到更好的效果,这里需要的数学证明比较多,就不说了哈。nlgn也是基于比较排序的算法的时间复杂度下限,也就是最优的。其中归并排序不是原址的,也就是会占用和问题大小相关的存储空间。其他三个算法都是原址的。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值