基本排序算法(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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值