美文网首页
快速排序 归并排序

快速排序 归并排序

作者: youtianlong123 | 来源:发表于2017-12-08 15:52 被阅读0次

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

快速排序

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);
}

相关文章

网友评论

      本文标题:快速排序 归并排序

      本文链接:https://www.haomeiwen.com/subject/mdveixtx.html