论秋招中的排序(排序法汇总-------中篇)

    续论秋招中的排序(排序法汇总-------上篇)(由于篇幅过大),下面我们继续学习。

    论秋招中的排序(排序法汇总-------上篇)

    论秋招中的排序(排序法汇总-------中篇)

    待续

    (原创,转发须注明原处)

    5、快速排序

    快速排序在面试中经常被问到(包括各种改进思路),此排序算法可以说是用得最为广泛的排序算法,是对冒泡排序的一种改进,每次交换是跳跃式的。其比较易于实现,同时它可以处理多种不同的输入数据,许多情况下所消耗的资源也较其他排序算法少。理想情况下,其只使用一个小的辅助栈,对N个数据项排序的平均时间复杂度为O(NlogN),并且它内部的循环很小,缺点就是不稳定的排序算法,在最坏的情况下时间复杂度为O(N^2)。其实过程很简单,想想就明白了,所以不想再细说,图解比语言更明确更精准,下面给出一轮划分的图解,接着递归两边的部分即可,图如下:

            

    即划分后,key=32的最终位置的左边的所有数据值都小于(或等于)key,右边的所有数据值都大于(或等于)key。

    代码如下:

int partion(int a[], int left, int right)//划分操作
{
    int lleft = left, rright = right, key = a[lleft];
    while (lleft < rright)
    {
        while (lleft < rright && a[rright] >= key)//找右边第一个小于key的
            rright--;
        a[lleft] = a[rright];
        while (lleft < rright && a[lleft] <= key)//找左边第一个大于key的
            lleft++;
        a[rright] = a[lleft];
    }
    a[lleft] = key;
    return lleft;//key的最终位置
}

    递归:

void Qsort(int a[], int left, int right)
{
    if (left >= right)//递归结束条件
        return;
    int index = partion(a, left, right);
    Qsort(a, left, index - 1);//递归key左边区域
    Qsort(a, index + 1, right);//递归key右边区域
}

    非递归:

void qsort(int a[], int left, int right)
{
    std::stack<int> st;
    if (left < right)
    {
        int mid = partion(a, left, right);
        if (left < mid - 1) //将左边边界入栈
        {
            st.push(left);
            st.push(mid - 1);
        }
        if (mid + 1 < right)//将右边边界入栈
        {
            st.push(mid + 1);
            st.push(right);
        }
        while (!st.empty()) //如果栈不为空,即排序未结束
        {
            int q = st.top();
            st.pop();
            int p = st.top();
            st.pop();
            mid = partion(a, p, q);//继续划分
            if (p < mid - 1)
            {
                st.push(p);
                st.push(mid - 1);
            }
            if (mid + 1 < q)
            {
                st.push(mid + 1);
                st.push(q);
            }
        }
    }
}

    在最坏的情况下,如果要排序的序列是有序的,则快速排序将退化为冒泡排序,时间复杂度变为O(N^2);在最好的情况下,每次划分过程都恰好把文件分割成两个大小完全相等的部分,则时间复杂度为O(NlogN)。

    

    改进算法:

    上面我们也提到了,在最坏的情况下,如果待排序列已经有序了,则快排会变得非常低效。下面将会介绍改进的方法:

    改进选取的参考枢纽元素:1、选取随机数作为枢轴。但是随机数的生成本身是一种代价,根本减少不了算法其余部分的平均运行时间。2、使用左端,右端和中心的中值做为枢轴元(即三者取中)。3、每次选取数据集中的中位数做枢轴。

    改进代码:

void exchange(int*a, int *b)
{
    int tmp;
    tmp = *a;
    *a = *b;
    *b = tmp;
}

void compexch(int* a, int* b)
{
    if (*a > *b)
        exchange(a, b);
}
int partion(int a[], int left, int right)//划分操作
{
    int lleft = left + 1, rright = right - 1;
    int key;
    //三者取中法
    int mid = (left + right) / 2;
    compexch(&a[left], &a[mid]);
    compexch(&a[left], &a[right]);
    compexch(&a[mid], &a[right]);
    key = a[mid];
    while (lleft < rright)
    {
        while (lleft < rright && a[rright] >= key)//找右边第一个小于key的
            rright--;
        a[lleft] = a[rright];
        while (lleft < rright && a[lleft] <= key)//找左边第一个大于key的
            lleft++;
        a[rright] = a[lleft];
    }
    a[lleft] = key;
    return lleft;//key的最终位置
}
void quicksort(int a[], int left, int right) //小区域排序采用插入排序方法(对于有序序列的排序效率较高O(N)),三者取中
{
    if (left >= right)//递归结束条件
        return;
    if (right - left <= 5)//当区域段长度小于5时,改用插入排序法
    {
        insertion(a, right-left+1);
        return;
    }
    int i;
    i = partion(a, left, right);
    quicksort(a, left, i - 1);
    quicksort(a, i + 1, right);
}

    下面我们继续考虑另外方面的改进,当待排序的序列中有大量的重复元素时,标准的快排又变得极其低效(哎呀,问题怎么这么多啊,烦不烦啊。。。。)。嘿嘿,当然,有解决的方法了,最直观的想法是将序列划分为三部分(三路划分)而不再是两部分了,即比划分元素小的部分、比划分元素大的部分和与划分元素相等的部分。

    三路划分仅仅在标准的快排下稍作改动:遍历时将遇到的左边区域中的与划分元素相等的元素放到序列的最左边,将遇到的右边区域中的与划分元素相等的元素放到序列的最右边。继而,当两个扫描的指针相遇时,序列中与划分元素相等的元素的位置就精确定位了。对于重复的元素的额外工作量只与所找到的重复的元素的个数呈线性相关;即使在没有重复的元素的情况下,在方法没有额外的开销,效果也很好。

    

    代码:

void quicksort(int a[], int left, int right) //小区域排序采用插入排序方法,三者取中,三路划分
{
    if (left >= right)
        return;
    int lleft = left - 1, rright = right , k, p=left-1, q=right;
    int key;
    if (right - left <= 1)
    {
        insertion(a, right-left+1);
        return;     //递归返回条件
    }
    int mid = (left + right) / 2;
    compexch(&a[left], &a[mid]);
    compexch(&a[left], &a[right]);
    compexch(&a[mid], &a[right]);
    key = a[right];
    while(true)
    {
        while (a[++lleft]< key);
        while (key< a[--rright])
        {
            if (rright == left)
            {
                break;
            }
        }
        if (lleft >= rright)
        {
            break;
        }
        exchange(&a[lleft], &a[rright]);
        if (a[lleft]== key)
        {
            p++;
            exchange(&a[p], &a[lleft]);
        }
        if (a[rright]== key)
        {
            q--;
            exchange(&a[q], &a[rright]);
        }
    }
    exchange(&a[lleft], &a[right]);
    lleft = lleft - 1;
    rright = lleft + 1;
    for (k = left; k <= p; k++, lleft--)
    {
        exchange(&a[k], &a[lleft]);
    }
    for (k = right - 1; k >= q; k--, rright++)
    {
        exchange(&a[k], &a[rright]);
    }
    quicksort(a, left, lleft);
    quicksort(a, rright, right);
}
时间: 2024-08-29 11:43:07

论秋招中的排序(排序法汇总-------中篇)的相关文章

MySQL中order by排序时,数据存在null咋办

order by排序是最常用的功能,但是排序有时会遇到数据为空null的情况,这样排序就会乱了,这里以MySQL为例,记录我遇到的问题和解决思路. 问题: 网页要实现table的行鼠标拖拽排序,我用AngularJs集成了一个TableDnD开源插件,可以实现,然后在数据库表中增加一个排序字段indexId,但是原来的大量数据是没有排序过的,所以该字段为null. 这样order by时,为null的数据就会排在最前边. 写个测试表模拟一下,如下效果: 解决办法: 最优办法:利用MySQL中的一

Basic4Android中按日期排序文件

#Region Project Attributes #ApplicationLabel: SortByDateFileTest #VersionCode: 20140627 #VersionName: 'SupportedOrientations possible values: unspecified, landscape or portrait. #SupportedOrientations: unspecified #CanInstallToExternalStorage: False

关于Oracle数据库中SQL空值排序的问题

在Oracle中进行查询排序时,如果排序字段里面有空值的情况下,排序结果可能会达不到自己想要的结果. 如 select * from tableTest order by VISITS desc 将原来的sql语句改写为:select * from tableTest order by VISITS desc nulls last,"nulls last"控制将空值记录放在后面,当然,你也可以用"nulls first"将控制记录放在前面. oracle 空值处理,

C实现数组中元素的排序

使用C实现数组中元素的排序,使得数组中的元素从小到大排列.只不过在这个过程中出了一点小问题,在C中进行数字交换的时候,必须要使用指针的,不能使用引用.在后面的文章中,我要学习一个在C中的引用和指针的区别.下面看一下我的代码吧. #include <stdio.h> void swap(int *a,int *b); void rest(int lels[],int count); /** * 该实例用于实现对用户输入的数组进行排序 * 要求的数组中的元素从小到大来咧 * * @brief ma

C#中List&lt;T&gt;排序

在面向对象开发过程中我们经常将一组对象放到一个特定集合中,此时我们通常使用泛型集合来存放,常见的如:List.Dictionary等.在使用这些泛型集合时我们有时需要对其进行排序,下面我们就一起学习下List<T>是如何进行排序的. 1.初始化集合 假如我们有一个Student对象,简单起见这个对象只有三个属性,分别是学号.姓名.年龄. namespace YDSLM_KT_SYSTEM.App_Code.Model{    public class Student    {        p

Java实现8中常用的排序

直接插入排序 import java.util.HashMap; /** * 直接插入排序 * @author HHF * 2014年3月19日 */ public class InsertSort { private static int contrastCount = 0;//对比次数 private static int swapCount = 0;//交换次数 public static void main(String[] args) { System.out.println("直接插

DataSet 中的数据排序 及 DataRow装成DataTable

1.DataSet 中的数据排序 DataSet ds = new DataSet(); // 获取当前排口的数据 ds = _xiaobill.GetHistoryData(yinZiBianm, zhanDian, beginDate, endDate, dNum); DataTable dt = ds.Tables[0]; DataRow[] dt2 = dt.Select("1=1","数据时间 ASC "); DataRow[]装成DataTable Da

Android中ListView字母排序,实现字母挤压效果以及右侧快速选中字母,搜索关键字功能

Android中ListView字母排序,实现字母挤压效果以及右侧快速选中字母,搜索关键字功能 本文中阐述如何自定义EditText实现搜索框自定义的样式以及挤压字母的思路等 自定义EditText 相关的drawable文件 主界面以及相关的适配器 结果展示 定义要呈现的EditText的样式 public class ClearEditText extends EditText implements OnFocusChangeListener, TextWatcher { /** * 定义删

网页中的信息排序信息显示

平常我们在浏览网页的时候,经常会见到下图所示的列表信息. 那么今天我们就来学习下网页中的信息排序显示 列表标记 1.    标记的用途 列表标记可以创建一般的无序列表.编号列表,以及定义列表三种方式.还可以在一种列表中嵌套另一种列表,便于概括显示一系列相关的内容. 2.    无序列表 语法: <ul type="项目符号类型"> <li type="项目符号类型">内容1</li> - </ul> 牛刀小试 <