排序大荟萃

  • 选择排序

    图像化显示:

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

时间复杂度=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)

  • 降序:与之相反

    图形化表示:

    下面是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)

    图形化显示:

    以下是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)

    图形化显示:

    以下是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(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]);
			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);
	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]);
			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(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);
	}
}
时间: 2024-07-30 12:44:27

排序大荟萃的相关文章

BitMap排序-大数据量节省空间

package com.jp.algorithm.sort; /** * 假设我们要对0-7内的5个元素(4,7,2,5,3)排序(这里假设这些元素没有重复).那么我们就可以采用Bit-map的方法来达到排序的目的.要表示8个数 * ,我们就只需要8个Bit(1Bytes),首先我们开辟1Byte的空间,将这些空间的所有Bit位都置为0 * 然后遍历这5个元素,首先第一个元素是4,那么就把4对应的位置为1. * 然后再处理第二个元素7,将第八位置为1,,接着再处理第三个元素,一直到最后处理完所有

极客Web前端开发资源大荟萃#002

每周极客都将总结本周最精彩的素材提供给大家,希望可以带给你更多地灵感和帮助!极客#GB课程库#现已上线,无论你是初级.中级.还是正在进修的高级前端工程师.这里都将帮助你得到更多更高效的学习. #GB课程库# 极客Web前端开发资源大荟萃#001 如何写出高性能的Jquery代码 讨论jQuery和javascript性能的文章并不罕见.然而,本文我计划总结一些速度方面的技巧和我本人的一些建议,来提升你的jQuery和javascript代码.好的代码会带来速度的提升.快速渲染和响应意味着更好的用

极客Web前端开发资源大荟萃#007

本周我们带来的前端推荐包含当前热门的bootstrap,html5,css3等技术内容和新闻话题,如果你还想近一步学习如何开发,还可以关注我们的极客课程库,里面涵盖了现代开发技术的'学'与'习'的全新功能.希望对大家有所帮助!原文来自:极客标签 移动设备表单输入设计体验 - leader.js 为了帮助降低移动或者手机端输入的操作复杂度,leader.js提供了一个相对更简单的输入体验,你只需要输入表单内容回车即可完成输入,非常方便. Javascript游戏,街头霸王 有没有让你想起点什么?我

极客Web前端开发资源大荟萃

每周极客都将总结本周最精彩的素材提供给大家.希望能够带给你很多其它地灵感和帮助.极客#GB课程库#现已上线,不管你是0基础.中级.还是正在进修的高级前端project师.这里都将帮助你得到很多其它更高效的学习. #GB课程库# 极客Web前端开发资源大荟萃#001 怎样写出高性能的Jquery代码 讨论jQuery和javascript性能的文章并不罕见.然而,本文我计划总结一些速度方面的技巧和我本人的一些建议.来提升你的jQuery和javascript代码.好的代码会带来速度的提升.高速渲染

极客Web前端开发资源大荟萃#001

每周极客都将总结本周最精彩的素材提供给大家,希望可以带给你更多地灵感和帮助!极客#GB课程库#现已上线,无论你是初级.中级.还是正在进修的高级前端工程师.这里都将帮助你得到更多更高效的学习.原文:极客Web前端开发资源大荟萃#001 超棒的HTML5/CSS3单页面响应式模板(支持Bootstrap) 在线演示 使用Bootstrap实现的响应式单页面模板,包含移动和桌面两种效果. jQuery框架开发一个最简单的幻灯效果 在线演示 在这个课程中,我们将介绍如何使用jQuery来开发一个最简单的

不可错过的前端开发资源大荟萃

本周我们带来的前端推荐包含当前热门的bootstrap,html5,css3等技术内容和新闻话题,如果你还想近一步学习如何开发,还可以关注我们的极客课程库,里面涵盖了现代开发技术的'学'与'习'的全新功能.希望对大家有所帮助!原文来自:极客标签 jQueryMobile 实现一个简单的弹出框效果 今天给大家带来的是 jQueryMobile 实现一个简单的弹出框效果,有兴趣的童鞋可以试试哦~ 深入浅出H5游戏开发原理 跟我一起从零开始,创造出一套简单又强大的HTML5游戏引擎吧! 分享10套古典

排序大总结

5.排序操作 5.1基础知识 对于排序操作,介绍过很多的算法,在笔记中有关于排序算法的简单总结,但是对于每一个配需算法没有详细的深入,知识对于排序的稳定性做了一下了解.下面深入的分析一下排序算法,并且使用 C++ 语言实现. 首先介绍上一次在笔记中整理稳定排序和不稳定排序的知识: 选择排序.快速排序.希尔排序.堆排序不是稳定的排序算法,而冒泡排序.插入排序.归并排序和基数排序是稳定的排序算法. 同时我们在分析一下关于排序算法的一些知识.对于排序,需要序列和关键字.关键字可能重复出现,这样排序的结

极客Web开发资源大荟萃

前端开发已经成为当前炙手可热的技术之一.此次我们总结的前端开发包含了相关技术和流行趋势,希望从中大家可以挖掘你们所需要的,并带给你们最有价值的帮助!原文来自:极客标签 使用代码回放来愉快地学习前端知识 - TECH2IPO创见 编 程学习中最痛苦的地方在于:你试图理解学习对象的思路,但常规教学材料的传播方式生硬,对编程新手来说一点都不体贴.极客标签是一个试图改变这 种局面的产品,你可以称呼它“编程学习平台”,但它比同类产品好得多.来自tech2ipo对极客标签的专业报道和介绍,欢迎大家阅读和了解

排序算法大荟萃——冒泡排序算法

1.基本思想:将无序数组R[1...n]垂直排列,从下往上扫描数组R,对比相邻的两个元素,如果上面的元素值小于下面的值,则调整这两个相邻元素的位置,然后继续向上扫描,直到排好序为止. 2.排序过程: (1)初始化:读取无序数组R[1...n] (2)第一趟扫描:从数组R底部开始向上扫描,依次比较相邻的两个元素,若发现数值较小的在上面,则交换两个元素的位置.即依次比较(R[n].R[n-1]).(R[n-1].R[n-2]).....(R[2].R[1]),对于每对元素(R[j].R[j+1]),