• 选择排序

    图像化显示: 选择排序动画演示

    wKiom1cHmPiRz7U7AACWdFi_Y8c312.png

选择排序的基本思想:从待排序序列中找到最小(大)的元素,存放到序列起始位置,缩小排序范围,再找当前序列最小(大)的元素,放在起始位置之后,直到所有数据都被排完。

时间复杂度=O(n^2)

空间复杂度=O(1)

最好情况:已经有序 交换次数O(1)

最坏情况:逆序 交换次数O(n-1)

下面是c++版本的代码实现

#include <iostream>
using namespace std;
//初始版本,每次找到最小的
void SelectSort(int *a,size_t size)
{
    //比较次数:n*(n-1)/2
    for(size_t i=0;i<size;i++)
    {
        
        int min=i;
        for(size_t j=i+1;j<size;j++)
        {
            //交换次数:0~n-1之间
            if(a[min]>a[j])
            {
                min=j;
            }
        }
        //赋值次数:0~3(n-1)之间
        if(min!=i)
        {
             swap(a[min],a[i]);
        }
        
    }
}

//改进版本,每次确定最大的和最小的
void SelectSort(int *a,size_t size)
{
    //1/2数列的长度
    for(size_t left=0,right=size-1;left<right;left++,right--)
    {
        int min=left;
        int max=right;
        for(size_t j=left+1;j<=right;j++)
        {
            if(a[min]>a[j])
            {
                min=j;
            }
            if(a[max]<a[j])
            {
                max=j;
            }
        }
        if(min!=left)
		{
		        //极端情况 左边最大 右边最小
			if(min==right&&max==left)
			{
				swap(a[min],a[left]);
			}
			//左边的最大 先将左边的移过去
			else if(min!=right&&max==left)
			{
				swap(a[max],a[right]);
				swap(a[min],a[left]);
			}
			else
			{
				swap(a[min],a[left]);
			}
		}
		if(max!=right)
		{
			swap(a[max],a[right]);
		}
    }
}
  • 堆排序

    堆排序的基本思想:利用堆这种数据结构进行选择排序。

    堆:堆是一个完全二叉树,其任意一个非叶子结点满足:

            (最大堆)a[key]>a[key*2+1]&&a[key]>a[key*2+2];(非叶子结点大于任意一个孩子结点)

            (最小堆)a[key]<a[key*2+1]&&a[key]<a[key*2+2]

    升序:由最大堆(根结点是所有结点中的最大值,任意一个结点都比它的孩子结点的值要大)将顶部元素与末尾元素交换,减小堆中元素个数,每次都将当前堆中最大的数排到正确的位置,直到只有一个根结点,这种通过二叉树的结构的排序,其n个数每次的找最大的数的执行次数是O(logn)即一个n个数的二叉树深度,共有n个数所以最终的时间复杂度是O(nlogn)

    时间复杂度:O(nlogn)

    空间复杂度:O(1)

  • 降序:与之相反

    图形化表示:堆排序算法的演示。首先,将元素进行重排,以符合堆的条件。图中排序过程之前简单的绘出了堆树的结构。

    wKioL1cKB5TwKvM8AAD0s8HR5so450.png下面是c++版本的代码实现

#include <iostream>
using namespace std;
//建局部堆----向下调整
void AdjustDown(int *a,int  size,int index)
{
    parent=index;
    int child=parent*2+1;
    while(child<size)
    {
        //如果a[child+1]存在,找a[child+1]和a[child]中的最大值
        if(child+1<size&&a[child+1]>a[child])
        {
            child++;
        }
        //如果孩子结点大于父结点,交换,并且看交换完之后,这个局部是否还满足条件
        if(a[child]>a[parent])
        {
            swap(a[child],a[parent]);
            parent=child;
            child=parent*2=1;    
        }
        //如果孩子结点不大于父节点,可以直接跳出
        else
        {
            break;
        }
    }
}
//堆排序
void HeapSort(int *a,int size)
{
    //将数组转化成堆----O(logn)
    for(int i=(size-2)/2;i>=0;i--)
    {
        AdjustDown(a,size,i);
    }
    //O(nlogn)
    for(int i=0;i<size;i++)
    {
        //交换堆顶和最后一个结点
        swap(a[size-1-i],a[0]);
        //堆得范围减小,并且建堆
        AdustDown(a,size-i-1,0);
    }
}
  • 直接插入排序

    大家都玩过扑克牌吧,直接插入排序,就是扑克牌抓牌调整的过程,一开始手上只有一张牌,不用调整,第二张牌来了,这个时候如果它比前面的那张小,且前面没有牌或者是前面的比你要插入的牌小就插入到比它小的牌后面或者最前面,假如(2,6,7)是你手上的牌,现在你抽到一张4,它比7小,继续比,直到找到比4小的的2,插在2后面

    时间复杂度:O(n^2)

    空间复杂度:O(1)

    最好情况下:已经是升序,需要比较n-1次

    最坏情况下:逆序,需要比较n*(n-1)/2次

    适用于:n比较小,n小于千次一下,插入排序较为有效

    以下是c++代码实现

#include <iostream>
using namespace std;
//直接插入排序
void InsertSort(int *a,size_t size)
{
  for(size_t i=1;i<size;i++)
  {
      int end=i-1;
      int temp=a[i];
      while(end>=0&&a[end]>temp)
      {
          a[end+1]=a[end];
          end--;
      }
      //当end所在位置的数比temp小时,while跳出循环,end指向带插入位置的前一个位置
      //所以end+1,才是temp该放的位置
      a[end+1]=temp;   
  }   
}
  • 希尔排序

    希尔排序是插入排序的优化版本,可以尽快的让小数往前走,大数往后走。

    方法:把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

    优点:对已经有序的序列,效率高,可以达到线性效率即O(n)

    缺点:每次只能移动一位(一般情况下的低效)

    时间复杂度:O(n^2)

    空间复杂度:O(1)

    图形化显示:

    wKiom1cKYbvwsSlnAABXrdSvUuE159.png

    以下是c++代码实现

#include <iostream>
using namespace std;
void ShellSort(int *a,int size)
{
    //增量值得设定
    int gap=size;
    while (gap>1)
	{
		gap=gap/3+1; 
		for (size_t i=gap;i<size;i++)
		{
			int index=i;
			int end=index-gap;
			int temp=a[index];
			while (end>=0&&temp<a[end])
			{
				a[end+gap]=a[end];
				end-=gap;
			}
			a[end+gap]=temp;
		}
	}
}
  • 归并排序

    归并排序应用的是分治法的思想,将一个无序序列,分成两部分,再将这两部分,看成子问题,在进行划分,一直到序列中只有一个数字的时候,当前子序列就有序了,然后将这些个子序列进行有序合并,知道合并成整个序列。速度仅次于快速排序,为稳定排序算法。

    简洁的说就是:无序序列-----划分成不可再分子序列----子序列有序合并----有序序列

    时间复杂度为O(nlogn) 这是该算法中最好、最坏和平均的时间性能。

    空间复杂度为 O(n)

    图形化显示:Example of merge sort sorting a list of random dots.

    以下是c++实现(递归和非递归版本)

//合并
void GetMerge(int *a,int begin1,int end1,int begin2,int end2,int *temp)
{
	int i=0;
	while(begin1<=end1&&begin2<=end2)
	{
		if (a[begin1]<a[begin2])
		{
			temp[i++]=a[begin1];
			begin1++;
		}
		else
		{
			temp[i++]=a[begin2];
			begin2++;
		}
	}
	while(begin1<=end1)
	{
		temp[i++]=a[begin1++];
	}
	while(begin2<=end2)
	{
		temp[i++]=a[begin2++];
	}
}
//分解
void _MergeSort(int *a,int left,int right)
{
	if (right-left<1)
	{
		return;
	}
	int mid=left+(right-left)/2;
	_MergeSort(a,left,mid);
	_MergeSort(a,mid+1,right);
	int *temp=new int[right-left+1];
	GetMerge(a,left,mid,mid+1,right,temp);
	memcpy(a+left,temp,(right-left+1)*sizeof(int));

}
//归并排序
void MergeSort(int *a,size_t size)
{
	_MergeSort(a,0,size-1);
}
void Print(int *a,size_t size)
{
	for (size_t i=0;i<size;i++)
	{
		cout<<a[i]<<" ";
	}
	cout<<endl;

}


//归并排序的非递归写法
void MergeSortNor(int *a,size_t size)
{
	//先分成一个一个的然后两两排序合并
	int temp[10];
	size_t begin=0;
	size_t gap=0;
	size_t end=begin+gap+1;
	for (gap;gap<size;gap+=begin-end)
	{
		for(begin=0;begin<size;begin+=gap*2+2)//如果有个是单着的???
		{
			end=begin+gap+1;
			if(begin+gap<size&&end<size&&end+gap<size)
			{
				GetMerge(a,begin,begin+gap,end,end+gap,temp+begin);
			}
			else
			{
				if(begin+gap<size)
				{
					if (end<size)
					{
						GetMerge(a,begin,begin+gap,end,size-1,temp+begin);
					}
					else
						//[8,9][9,9]----数组越界
						//[8,9][10,9]
						GetMerge(a,begin,begin+gap,size,size-1,temp+begin);
				}
				else
				{
					//[8,9][9,9]
					//[8,9][10,9]
					GetMerge(a,begin,size-1,size,size-1,temp+begin);
				}
			}
		}
		memcpy(a,temp,sizeof(int)*size);
	}
	
}
  • 冒泡排序

算法思想:

  1. 冒泡排序算法的运作如下:(从后往前)

  2. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。

  3. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。

  4. 针对所有的元素重复以上的步骤,除了最后一个。

  5. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

优点:稳定性算法

缺点:时间复杂度高

时间复杂度:O(N^2)(平均时间复杂度)

空间复杂度:O(1)

以下是c++代码

void BubbleSort(int *a1,size_t size)
{
	for (size_t i=0;i<size;i++)
	{
		for (size_t j=0;j<size-i-1;j++)
		{
			if (a[j]>a[j+1])
			{
				swap(a[j],a[j+1]);
			}
		}
	}
}
  • 快速排序

快速排序是对冒泡排序的一种改进。

它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。其中作为分隔数据的关键数据我们成为Key,当key为当前数列中的最大值时,快速排序就是冒泡排序.

最好情况:O(nlogn)

最坏情况:O(n^2)

时间复杂度:O(nlogn)

空间复杂度:O(1)

使用快速排序法对一列数字进行排序的过程

C++代码实现

(1)基础版---选择数列的最后一个数作为key,递归快排

//单趟排序
int PartionSort(int *a1,int left,int right)
{
	int begin=left;
	int end=right-1;
	int key=a[right];
	while(begin<end)
	{
		while(begin<end&&a[begin]<key)
		{
			begin++;
		}
		while(end>begin&&a[end]>key)
		{
			end--;
		}
		if (end!=begin)
		{
			swap(a[end],a[begin]);
			end--;
			begin++;
		}
	}
	if (a[begin]>key)
	{
		swap(a[begin],a[right]);
		return begin;
	}
	else
	{
		return begin;
	}
}
//快速排序--递归思想
void QuickSort(int *a1,int left,int right)
{
	assert(a);
	if (left<right)
	{
		int boundary=PartionSort(a,left,right);
		QuickSort(a,left,boundary-1);
		QuickSort(a,boundary+1,right);
	}
}

(2)改进版:选择key值时,用三数取中,避免最坏情况的发生。

//单趟排序
int PartionSort(int *a1,int left,int right)
{
	int begin=left;
	int end=right-1;
	int key=a[right];
	while(begin<end)
	{
		while(begin<end&&a[begin]<key)
		{
			begin++;
		}
		while(end>begin&&a[end]>key)
		{
			end--;
		}
		if (end!=begin)
		{
			swap(a[end],a[begin]);
		}
	}
	if (a[begin]>key)
	{
		swap(a[begin],a[right]);
		return begin;
	}
	else
	{
		return right;
	}
}

//快速排序---三数取中
void QuickSort(int *a1,int left,int right)
{
	assert(a);
	int mid=left+(right-left)/2;
	if (left<right)
	{
		if (a[left]<a[right])
		{
			if (a[right]<a[mid])
			{
				swap(a[mid],a[right]);
			}
			if (a[left]>a[mid])
			{
				swap(a[mid],a[left]);
			}
		}
		else
		{
			if (a[left]<a[mid])
			{
				swap(a[mid],a[left]);
			}
			if (a[right]>a[mid])
			{
				swap(a[mid],a[right]);
			}
		}
		swap(a[mid],a[right]);
		int boundary=PartionSort(a,left,right);
		QuickSort(a,left,boundary-1);
		QuickSort(a,boundary+1,right);
	}
}

(3)对每次排序进行优化,当数列中个数小于13(STL中这样实现的),应用插入排序。

//单趟排序
int PartionSort(int *a1,int left,int right)
{
	int begin=left;
	int end=right-1;
	int key=a[right];
	while(begin<end)
	{
		while(begin<end&&a[begin]<key)
		{
			begin++;
		}
		while(end>begin&&a[end]>key)
		{
			end--;
		}
		if (end!=begin)
		{
			swap(a[end],a[begin]);
		}
	}
	if (a[begin]>key)
	{
		swap(a[begin],a[right]);
		return begin;
	}
	else
	{
		return right;
	}
}
//快速排序中个数少时用插入排序
void QuickSort(int *a1,int left,int right)
{
	assert(a);
	if(right-left<13)
	{
		InsertSort(a,right-left+1);
	}
	if (left<right)
	{
		int boundary=PartionSort(a,left,right);
		QuickSort(a,left,boundary-1);
		QuickSort(a,boundary+1,right);
	}
}

(4)快排的非递归版本(栈实现)

//快速排序非递归实现
//手动利用栈来存储每次分块快排的起始点,栈非空时循环获取中轴入栈。
void QuickSort(int *a1,int left,int right)
{
	stack<int> s1;
	if(left<right)
	{
		int boundary=PartionSort(a,left,right);
		if (boundary-1-left>1)
		{
			s1.push(left);
			s1.push(boundary-1);
		}
		if (right-(boundary+1)>1)
		{
			s1.push(boundary+1);
			s1.push(right);
		}
		while (!s1.empty())
		{
			int r=s1.top();
			s1.pop();
			int l=s1.top();
			s1.pop();
			boundary=PartionSort(a,l,r);
			if (boundary-1-l>1)
			{
				s1.push(l);
				s1.push(boundary-1);
			}
			if (r-(boundary+1)>1)
			{
				s1.push(boundary+1);
				s1.push(r);
			}
		}
	}
}

(5)模拟单链表中的快排

int PartionSortLink(int *a1,int left,int right)
{
	int prev=left-1;
	int cur=left;
	int key=a[right];
	while(cur<right)
	{
		if (a[cur]<key)
		{

			prev++;
			if (prev!=cur)
			{
				swap(a[prev],a[cur]);
			}
		}
		if (cur==right-1)
		{
			prev++;
			swap(a[prev],a[right]);

		}
		cur++;
	}
	return prev;
}
void QuickSortLink(int *a1,int left,int right)
{
	assert(a);
	if (left<right)
	{
		int boundary=PartionSortLink(a,left,right);
		QuickSortLink(a,left,boundary-1);
		QuickSortLink(a,boundary+1,right);
	}
}