快速排序 归并排序

这两个排序算法放在一起的原因是因为他们的执行效率都很高,并且更重要的是他们的实现思路都是利用分治法进行实现的。分治法的意思大致就是把复杂问题细分成很多个简单的问题,逐个解决简单的问题,再将每个小问题的结果合并,这样大的复杂的问题也就得到了解决

快速排序

Java实现
    /***
     *       3  9  4  2  8  7  1  5  6
     *  思路:取数组第一个数为基准,在数组的头和尾设置两个指针。
     *        开始遍历,先比较尾指针的值和基准数的大小,如果大,则尾指针前移一位。
     *        否则,把尾指针的值放到头指针位置,头指针后移一位。尾指针停止,头指针开始移动。
     *        还是和基准数比较大小,如果小,则头指针后移一位。否则,将当前值放置到尾指针处,尾指针前移一位,尾指针开始移动。
     *        反复执行此操作,直到头尾指针相遇,将基准数放置在指针的相遇处。此时基准数的左边都是小于它的数字,右边都是大于它的数。
     *        再分别将它的左边和右边当成一个独立的数组执行上面的操作。细分的数组长度就每次以一半的速度递减。直到数组排序完成
     *  时间复杂度:O(nlogn)
     *  3    1  9  4  2  8  7  3  5  6
     *       1  3  4  2  8  7  9  5  6
     *       1  2  4  3  8  7  9  5  6
     *       1  2  3  4  8  7  9  5  6
     */
    private static void quickSort(int[] arr, int leftIndex, int rightIndex) {
        if ((rightIndex - leftIndex) < 1) return;
        int left = leftIndex;
        int right = rightIndex;
        // 设置数组中第一个数字为基准数,因为基准数是数组的最左边,所以从最右边向最左边开始遍历
        boolean directionLeft = true;
        int flag = arr[leftIndex];

        while (leftIndex != rightIndex) {
            if (directionLeft) {
                // right to left
                if (arr[rightIndex] > flag) {
                    rightIndex--;
                } else {
                    arr[leftIndex++] = arr[rightIndex];
                    directionLeft = !directionLeft;
                }
            } else {
                // left to right
                if (arr[leftIndex] < flag) {
                    leftIndex++;
                } else {
                    arr[rightIndex--] = arr[leftIndex];
                    directionLeft = !directionLeft;
                }
            }
        }

        arr[leftIndex] = flag;
        quickSort(arr, left, leftIndex - 1);
        quickSort(arr, leftIndex + 1, right);
    }
C实现
void quickSort(int *arr, int left, int right){
    if ((right - left) < 1)
    {
        return;
    }
    int flag = arr[left];
    int pLeft = left;
    int pRight = right;
    // orientation为1:从右向左  为0:从左向右
    int orientation = 1;

    while (pLeft < pRight)
    {
        if (orientation)
        {
            if (arr[pRight] > flag)
            {
                pRight--;
            }
            else
            {
                arr[pLeft++] = arr[pRight];
                orientation = 0;
            }
        }
        else
        {
            if (arr[pLeft] < flag)
            {
                pLeft++;
            }
            else
            {
                arr[pRight--] = arr[pLeft];
                orientation = 1;
            }
        }
    }
    arr[pLeft] = flag;

    quickSort(arr, left, pLeft - 1);
    quickSort(arr, pLeft + 1, right);
}

快速排序速度虽然快,但是存在一些使用上的限制,如果待排序的数组结构不是顺序结构,也就是数组结构。而是单链表结构的数据,那么指针的前移将是它的致命缺点,此时就需要归并排序来解决此问题

Java实现

此方法是将一个数组中的left到right进行排序,但是此数组需要一些特殊条件,就是left到mid和mid到right一定是两个有序的小数组。将left-mid和mid-right生成两个新的小数组。每个小数组维护一个指针指向数组头部,比较两个指针所指向的值。哪个值小,就从原数组的left处开始放置。指针后移,直到其中一个数组没有数据了。如果另一个小数组中还有剩余数据,将剩余数据继续放置到原数组中。执行完此方法,数组中从left到right处的数据就完成了排序

    /**
     * 1  2  3  5  7    2  4  6  8      
     */
    private static void merge(int[] arr, int left, int mid, int right) {
        int[] leftArr = new int[mid - left];
        int[] rightArr = new int[right - mid + 1];

        for (int i = left; i < mid; i++) {
            leftArr[i - left] = arr[i];
        }

        for (int i = mid; i < right + 1; i++) {
            rightArr[i - mid] = arr[i];
        }

        int pLeft = 0;
        int pRight = 0;
        int pArr = left;

        while (pLeft < leftArr.length && pRight < rightArr.length) {
            if (leftArr[pLeft] <= rightArr[pRight]) {
                arr[pArr++] = leftArr[pLeft++];
            } else {
                arr[pArr++] = rightArr[pRight++];
            }
        }

        if (pLeft < leftArr.length) {
            for (int i = pLeft; i < leftArr.length; i++) {
                arr[pArr++] = leftArr[i];
            }
        }

        if (pRight < rightArr.length) {
            for (int i = pRight; i < rightArr.length; i++) {
                arr[pArr++] = rightArr[i];
            }
        }
    }

递归调用将数组细分成很多小数组,按位置排序合并后再让较大的数组合并排序

    private static void mergeSort(int arr[], int left, int right) {
        if (right == left) return;
        int mid = (left + right) / 2;
        mergeSort(arr, left, mid);
        mergeSort(arr, mid + 1, right);
        merge(arr, left, mid + 1, right);
    }
C 实现 (单链表数据结构)
typedef struct NODE
{
    NODE *next;
    int data;
}Node;

/*
创建链表:随机生成10个数据
*/
void createList(Node *head){
    Node *pNode = head;
    for (int i = 0; i < 10; i++)
    {
        Node *node = (Node *)malloc(sizeof(Node));
        node->data = rand()%10+1;
        node->next = NULL;

        pNode->next = node;
        pNode = node;
    }
}

/*
遍历链表
*/
void printList(Node *head){
    Node *pNode = head;
    while (pNode->next != NULL)
    {
        pNode = pNode->next;
        printf("%d\t",pNode->data);
    }
}

/*
将链表从mid分为两个有序子链表,再把两个有序子链表排序放回原链表中
*/
void merge(Node *head,int left,int mid,int right){
    // 创建两个新的链表
    Node *leftHead = (Node *)malloc(sizeof(Node));
    Node *rightHead = (Node *)malloc(sizeof(Node));
    Node *leftControlNode = leftHead;
    Node *rightControlNode = rightHead;
    Node *pNode = head;
    // 移动指针到原链表的left处
    for (int i = 0; i < left; i++)
    {
        pNode = pNode->next;
    }
    // 从原链表的left处开始遍历原链表,将left和Mid之间node加入到left链表中。将right和mid之间的node加入到right链表中
    for (int i = left; pNode->next != NULL && i < right + 1; i++)
    {
        pNode = pNode->next;
        if (i < mid)
        {
            Node *node = (Node *)malloc(sizeof(Node));
            node->data = pNode->data;
            node->next = NULL;

            leftControlNode->next = node;
            leftControlNode = node;
        }
        else{
            Node *node = (Node *)malloc(sizeof(Node));
            node->data = pNode->data;
            node->next = NULL;

            rightControlNode->next = node;
            rightControlNode = node;
        }
    }

    // 将两个左右链表排序并合并:设置两个指针分别指向链表第一个node,比较值,较小的加入大链表的left处,指针后移
    // 直到某一个遍历完毕,将另一个链表剩余的node依次加入到大链表中,完成排序
    Node *pLeft = leftHead->next;
    Node *pRight = rightHead->next;

    Node *pNodeHead = head;
    // 移动指针到大链表的left处
    for (int i = 0; i < left; i++)
    {
        pNodeHead = pNodeHead->next;
    }

    while (pLeft != NULL && pRight != NULL)
    {
        if (pLeft->data <= pRight->data)
        {
            pNodeHead->next = pLeft;
            pNodeHead = pLeft;
            pLeft = pLeft->next;
        }
        else{
            pNodeHead->next = pRight;
            pNodeHead = pRight;
            pRight = pRight->next;
        }
    }

    // left链表中还有剩余node,加入到大链表中
    while (pLeft != NULL)
    {
        pNodeHead->next = pLeft;
        pNodeHead = pLeft;
        pLeft = pLeft->next;
    }

    // right链表中还有剩余node,加入到大链表中
    while (pRight != NULL)
    {
        pNodeHead->next = pRight;
        pNodeHead = pRight;
        pRight = pRight->next;
    }

    // 将原链表right之后的节点添加到新生成的链表中
    pNode = pNode->next;
    while (pNode != NULL)
    {
        pNodeHead->next = pNode;
        pNodeHead = pNode;
        pNode = pNode->next;
    }
}

// 排序
void mergeSort(Node *head,int left,int right){
    if ((right - left) < 1)
    {
        return;
    }
    int mid = (left + right) / 2;
    mergeSort(head,left,mid);
    mergeSort(head,mid+1,right);
    merge(head,left,mid+1,right);
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容

  • 1.快速排序 快速排序每趟选择一个基准元素,用基准元素将序列划分成两部分,所有比基准值小的元素摆放在基准前面,所有...
    游戏开发小Y阅读 202评论 0 0
  • 1.快速排序 快速排序每趟选择一个基准元素,用基准元素将序列划分成两部分,所有比基准值小的元素摆放在基准前面,所有...
    sunnygarden阅读 288评论 0 2
  • 分治策略 本文包括分治的基本概念二分查找快速排序归并排序找出伪币棋盘覆盖最大子数组 源码链接:https://gi...
    廖少少阅读 1,832评论 0 7
  • 分治法,是算法思想里最基础的思想。这也和人的基本思维有关,当我们需要解决一个大的问题时,直觉的就会将这个大问题分成...
    一口咖啡一口茶阅读 3,865评论 1 18
  • 昨天有点累,作业就暂时拖一天吧。 如果真的有篮球迷和我每天针对这史诗般的赛季进行探讨的话,每天的作业就不会这么难交...
    荷拉最爱阅读 201评论 0 0