基本排序算法(1)

总览:简单分类,以便记忆:

A.  快速排序 其实是 冒泡排序 的升级,它们都属于交换(比较、移动交换)排序类;(增大了数据 比较和移动 的距离,减少了比较次数 和 移动交换次数)

B.  希尔排序 其实是 直接插入排序 的升级,它们都属于插入排序类;(划分增量子序列,在子序列中进行直接插入排序)

C.  堆排序 其实是 选择排序 的升级,它们都属于选择排序类;

1、冒泡排序:

冒泡排序的 基本思想是:两两比较相邻的数据,如果反序,则交换;知道没有反序位置。每一趟都会把一个数字放到最终的位置上!时间复杂度O(N^2)。

// 冒泡排序
/*
 * 特别注意内层循环:
 *(1)另外一种写法:
      for (vector<int>::iterator i = data.begin(); i != data.end() && swapFlag; i++)
      for (vector<int>::iterator j = data.end() - 2; j >= i; --j)     
      if (*j > *(j + 1))      // “j=data.end()-2” 的原因,防止越界

      if (j == i)     // 第一趟循环时i是begin(),如果不进行特殊处理,--j会造成vector的越界。
          break;

 *(2)if (j == i)
           break;
*/
void bubbleSort(vector<int>& data)
{
    if (data.size() == 0)
    {
        return ;
    }

    bool swapFlag = true;    // 冒泡排序优化:当“某”过程中 未 发生交换时,则停止整个排序过程(因为是相邻数据两两比较)

    for (vector<int>::iterator i = data.begin() + 1; i != data.end() && swapFlag; i++)    //注意:i = data.begin() + 1
    {
        swapFlag = false;
        for (vector<int>::iterator j = data.end() - 1; j >= i; --j)    // end()指向的是最后一个元素的下一个位置
        {
            if (*j < *(j - 1))
            {
                std::swap(*j, *(j - 1));   // 升序
                swapFlag = true;    // 如果有数据交换,则swapFlag为true
            }    
        }
    }
}
void bubbleSort2(int *data, int length)
{
    bool swapFlag = true;     // 冒泡排序优化:当某趟过程中未发生交换,则停止整个排序过程(因为是相邻数据两两比较)
    for (int i = 1; i < length && swapFlag; i++)     // 注意:i = 1;
    {
        swapFlag = false;
        for (int j = length - 1; j >= i; j--)    // j从后往前循环
        {
            if (data[j] < data[j - 1])
            {
                std::swap(data[j], data[j - 1]);
                swapFlag = true;    // 如果有数据交换,则swapFlag为true
            }
        }
    }
}

2、选择排序:

选择排序 的基本思想是:每一趟在 n - i + 1 个记录中选取最小值(n-i次比较),然后和第i个记录交换(有序序列的第i个记录)。时间复杂度O(N^2)。但是减少了交换移动次数。(冒泡排序的思想是在不断地进行交换)

// 选择排序
void selectSort(vector<int>& data)
{
    int min = 0;
    for (int i = 0; i < data.size(); i++)
    {
        min = i;
        //每一趟在 n - i + 1 个记录中选取最小值(n - i次比较),然后和第 i 个记录交换(即这个最小值作为有序序列的第i个记录)
        for (int j = i + 1; j < data.size(); j++)     
        {
            if (data[j] < data[min])
            {
                min = j;                 // 选择出最小值的下标
            }
        }

        if (i != min)                    // 交换
        {
            std::swap(data[i], data[min]);
        }
    }
}

3、快速排序

快速排序 其实是 冒泡排序 的升级,它们都属于交换(比较、交换移动)排序,都是通过不断比较和移动交换来实现排序的。但是,快速排序增大了数据 比较和交换移动 的距离(不再是相邻数据两两比较),将关键字较大的记录从前面直接交换移动到后面,关键字较小的记录从后面直接交换移动交换到前面,减少了比较次数
和 交换移动次数。


快速排序 的基本思想
:通过一趟排序,将待排记录分割成独立的两部分,其中一部分记录均比另外一部分的记录小。然后对这两部分递归上述步骤,直到整个序列有序。

每一趟都会把基准元素放到最终位置。

快速排序也是基于分治处理的,时间复杂度最好情况下(递归树划分较均匀):O(N * logN),很显然是取决于递归的深度。最坏情况是,待排序列为正序或者逆序,每次划分只得到一个比上一次划分少一个记录的子序列,注意另一个为空(也就是一颗斜树)。

版本1:

//快速排序版本1
void quickSort1(int *data, int left, int right)
{
    if (left < right)
    {
        int low = left;
        int high = right;
        int key = data[low];    // 基准元素

        while (low < high)      // 从数据表的两端交替向中间扫描
        {
            while (low < high && data[high] >= key)   // 从右向左找第1个小于基准值的位置high
                high--;
            if (low < high)               // 找到了
            {
                data[low] = data[high];   // 采用替换而不是交换
                low++;
            }
            while (low < high && data[low] <= key)    // 从左向右找第1个大于基准值的位置low
                low++;
            if (low < high)               // 找到了
            {
                data[high] = data[low];   // 采用替换而不是交换
                high--;
            }
        }

        data[low] = key;        // 将基准元素放入最终位置,本次划分结束

        quickSort1(data, left, low - 1);     // 对左半部递归
        quickSort1(data, low + 1, right);    // 对右半部递归
    }
}

版本2:

//快速排序版本2
int partition(int *data, int low, int high)
{
	//partition函数就是要把关键字(枢轴)——data[low]放到最终位置上。(使得它左边的值都比它小,右边的值都比它大)
	int key = data[low];
	while (low < high)
	{
		while (low < high && data[high] >= key)
			high--;
		std::swap(data[low], data[high]);   // 比枢轴小的记录交换到低端

		while (low < high && data[low] <= key)
			low++;
		std::swap(data[low], data[high]);   // 比枢轴大的记录交换到高端
	}

	return low;    // 返回关键字所在位置
}

void quickSortCore(int *data, int low, int high)
{
	int pivot;
	if (low < high)
	{
		pivot = partition(data, low, high);

		quickSortCore(data, low, pivot - 1);
		quickSortCore(data, pivot + 1, high);
	}
}

void quickSort2(int *data, int length)
{
	quickSortCore(data, 0, length - 1);
}

4、直接插入排序:

直接插入排序 的基本思想是:将一个数据插入到前面已经排好序的有序表中,从而得到一个新的、记录数增1的新有序表。每趟保证前面部分有序!时间复杂度O(N^2)。直接插入排序适合于数据表本身基本有序的情况。

// 直接插入排序
void insertSort(int *data, int length)
{
    for (int i = 1; i < length; i++)
    {
        if (data[i] < data[i - 1])    // 优化:如果需要将data[i]插入前面的有序子表,才执行
        {
            int tmp = data[i];        // 哨兵
            int j;

            // 将数据插入到前面已经排好序的有序表中,从而得到一个新的、记录数增1的新有序表
            for (j = i - 1; j >= 0 && tmp < data[j]; --j)    
            {
                data[j + 1] = data[j];    // 数据后移
            }
            data[j + 1] = tmp;        // 插入到正确位置
        }
    }
}

5、希尔排序:

我们知道上面的直接插入排序适合于 数据表中数据较少 或者 数据表本身基本有序 的的特殊情况。

希尔排序  的基本思想是:将现实中的大量数据分割成若干子序列,在这些子序列内进行直接插入排序,最后再对全体记录(此时整个序列已经“基本有序”——较小的关键字在前面,不大不小的关键字在中间,较大的关键字在后面)进行一次直接插入排序。

希尔排序的关键在于分割!我们通常采用“跳跃分割策略”——将相距某个“增量”的记录组成一个子序列,这样才能保证在各个子序列内分别进行直接插入排序后,得到的结果是“基本有序”而不是“局部有序”!(初始序列:9,1,5,8,3,7,4,6,2)(局部有序:{1,5,9}、{3,7,8}、{2,4,6})(基本有序:{2,1,3}、{6,4,7}、{5,8,9})

这里的增量子序列的步长是关键问题!也就说希尔排序的子序列并不是随便分组各自排序,而是将相隔某个“增量”的记录组成一个子序列,实现跳跃式的移动,从而提高效率。

// 希尔排序
void shellSort(int *data, int length)
{
    //int num = 1, count = 1;

    int disLen = length;
    while (disLen > 1)
    {
        disLen = disLen / 2;   // 增量子序列, “增量步长”是 disLen / 2
        for (int i = disLen; i < length; i++)
        {
            if (data[i] < data[i - disLen])
            {
                int tmp = data[i];            // 待排元素

                // 直接插入排序
                int j;
                for (j = i - disLen; j >= 0 && tmp < data[j]; j -= disLen)    // 寻找data[i]的插入位置
                {
                    data[j + disLen] = data[j];    // 数据后移
                }
                data[j + disLen] = tmp;        // 插入
                
            }
            
            /*cout << "第" << num++ << "次排序:";
            for (int i = 0; i < length; i++)
            {
                cout << data[i] << " ";
            }
            cout << endl;*/
        }

        /*cout << endl;
        cout << "第" << count++ << "趟排序:" <<endl;
        for (int i = 0; i < length; i++)
        {
            cout << data[i] << " ";
        }
        cout << endl << endl;;*/
    }
}

运行:(排序详细过程)

6、归并排序:

归并排序 的基本思想:把待排序列的N个记录看成N个有序的子序列,然后进行两两归并;然后对N/2 个子序列继续两两归并,直到N个记录全部有序。

归并排序也是基于分治的,时间复杂度:O(N * logN)。

//将二个有序数列 a[first...mid] 和 a[mid...last] 合并。
void mergearray(int a[], int first, int mid, int last, int temp[])
{
	int i = first, j = mid + 1;
	int m = mid, n = last;
	int k = 0;

	while (i <= m && j <= n)
	{
		if (a[i] <= a[j])
			temp[k++] = a[i++];
		else
			temp[k++] = a[j++];
	}

	while (i <= m)
		temp[k++] = a[i++];

	while (j <= n)
		temp[k++] = a[j++];

	for (i = 0; i < k; i++)
		a[first + i] = temp[i];
}
void mergesortCore(int a[], int first, int last, int temp[])
{
	if (first < last)
	{
		int mid = (first + last) / 2;
		mergesortCore(a, first, mid, temp);       //左边有序
		mergesortCore(a, mid + 1, last, temp);    //右边有序
		mergearray(a, first, mid, last, temp);    //再将二个有序数列合并
	}
}

//“分解”——将序列每次折半划分
//“合并”——将划分后的序列段两两合并后排序
bool mergeSort(int a[], int n)
{
	int *p = new int[n];
	if (p == NULL)
		return false;
	mergesortCore(a, 0, n - 1, p);
	delete[] p;
	return true;
}
时间: 2024-08-24 15:53:40

基本排序算法(1)的相关文章

经典排序算法 - 冒泡排序Bubble sort

 原文出自于 http://www.cnblogs.com/kkun/archive/2011/11/23/bubble_sort.html 经典排序算法 - 冒泡排序Bubble sort 原理是临近的数字两两进行比较,按照从小到大或者从大到小的顺序进行交换, 这样一趟过去后,最大或最小的数字被交换到了最后一位, 然后再从头开始进行两两比较交换,直到倒数第二位时结束,其余类似看例子 例子为从小到大排序, 原始待排序数组| 6 | 2 | 4 | 1 | 5 | 9 | 第一趟排序(外循环) 第

排序算法比较及其应用

一.将各种数据排序 只要实现了Comparable接口的数据类型就可以被排序. 但要使算法能够灵活地用不同字段进行排序,则是后续需要考虑的问题. 1.指针排序 在Java中,指针操作是隐式的,排序算法操作的总是数据引用,而不是数据本身. 2.键不可变 如果在排序后,用例还可以改变键值,那么数组很可能就不是有序的了.类似,优先队列也会乱套. Java中,可以用不可变数据类型作为键来避免这个问题,如String,Integer,Double和File都是不可变的. 3.廉价交换 使用引用的另一个好处

选择排序 —— 排序算法系列

假设我们有如下一个数组: 使用选择排序算法对这个数组进行排序,步骤如下: 第 1 次 在下标0到6之间找到最小的数字,我们可以发现最小的数字是15,它在下标为4的位置上: 把下标4上面的数字跟下标0上面的数字互换,得到排序如下图的数组: 第 2 次 在下标1到6之间找到最小的数字,我们可以发现最小的数字是33,它在下标为5的位置上: 把下标5上面的数字跟下标1上面的数字互换,得到排序如下图的数组: 第 3 次 在下标2到6之间找到最小的数字,我们可以发现最小的数字是48,它在下标为5的位置上:

排序算法Java版,以及各自的复杂度,以及由堆排序产生的top K问题

常用的排序算法包括: 冒泡排序:每次在无序队列里将相邻两个数依次进行比较,将小数调换到前面, 逐次比较,直至将最大的数移到最后.最将剩下的N-1个数继续比较,将次大数移至倒数第二.依此规律,直至比较结束.时间复杂度:O(n^2) 选择排序:每次在无序队列中"选择"出最大值,放到有序队列的最后,并从无序队列中去除该值(具体实现略有区别).时间复杂度:O(n^2) 直接插入排序:始终定义第一个元素为有序的,将元素逐个插入到有序排列之中,其特点是要不断的 移动数据,空出一个适当的位置,把待插

排序算法总结

各种排序算法总结  排序算法  插入排序 冒泡排序  选择排序  归并排序  快速排序 堆排序  计数排序  基数排序  桶排序  思想  构建有序序列 两两交换 每次找一个最小值 分治法思想 分治法思想 最小堆.最大堆 数字本身的属性  对数据选择多种基数  函数的映射关系.Hash  数据结构  数组  数组  数组  数组 不定   数组 数组 数组  数组  最差时间复杂度 O(n^2)   O(n^2)   O(n^2)   O(n*lgn)  O(n^2).改进O(n*lgn)  O

七大常见排序算法总结

文档版本 开发工具 测试平台 工程名字 日期 作者 备注 V1.0 2016.04.06 lutianfei none V1.1 2016.07.16 lutianfei 增加了归并排序说明 V2.0 2016.07.19 lutianfei 完善了排序算法的总结 排序另一种分法 外排序:需要在内外存之间多次交换数据才能进行 内排序: 插入类排序 直接插入排序 希尔排序 选择类排序 简单选择排序 堆排序 交换类排序 冒泡排序 快速排序 归并类排序 归并排序 排序方法 平均情况 最好情况 最坏情况

数据结构——各排序算法的比较

1.从时间复杂度比较  从平均时间复杂度来考虑,直接插入排序.冒泡排序.直接选择排序是三种简单的排序方法,时间复杂度都为O(n2),而快速排序.堆排序.二路归并排序的时间复杂度都为O(nlog2n),希尔排序的复杂度介于这两者之间.若从最好的时间复杂度考虑,则直接插入排序和冒泡排序的时间复杂度最好,为O(n),其它的最好情形同平均情形相同.若从最坏的时间复杂度考虑,则快速排序的为O(n2),直接插入排序.冒泡排序.希尔排序同平均情形相同,但系数大约增加一倍,所以运行速度将降低一半,最坏情形对直接

八种排序算法

最近一段时间自己在研究各种排序算法,于是自己写了一个八种排序算法的集合: /************************************************************************* > Copyright (c)2014 stay hungry,stay foolish !!! > File Name: sort.cpp > Author: kanty > Mail: [email protected] > Created Time:

排序算法 之 快速排序

快速排序是基于分治思想的一种排序算法,就像该方法的名字一样,速度比较快,所以叫做快速排序:它的平均时间复杂度为O(N*logN),最坏时间复杂度为O(n2),由于快速排序在序列元素数量多的时候速度比较快,所以很多语言内置的排序方法也是用快速排序实现的.快速排序也有很多优化的版本,比如在排序时基数的选择等等-下面就说一下一般的快速排序的实现. 基本思想: 快速排序的基本思想就是,先从待排序的序列中任选一个元素作为基数,然后将序列中的其他小于基数的元素放在基数的左边,大于或等于基数的元素放在基数的右

排序算法的JS实现

排序算法是基础算法,虽然关键在于算法的思想而不是语言,但还是决定借助算法可视化工具结合自己常用的语言实现一下 1.冒泡排序 基本思路:依次比较两两相邻的两个数,前面数比后面数小,不变.前面数比后面数大,交换顺序.一轮下来,最后的一个数是最大的数. 外循环每增加一次,内循环减少一次. 图形展示: function bubbleSort(arr){ for (var i = 0; i < arr.length; i++) { for (var j = 0; j< arr.length-i-1; j