zoukankan      html  css  js  c++  java
  • 归并排序

    归并排序,典型的分治思想,时间复杂度为O(nlogn)。

    一、数组

    递归:

    void MergeSort(int* array, int low, int high, int* auxiliary_array)
    {
        if (low >= high) return;
    
        int mid = low + (high - low) / 2;
        MergeSort(array, low, mid, auxiliary_array);
        MergeSort(array, mid + 1, high, auxiliary_array);
    
        int i = low;
        int j = mid + 1;
        int index = 0;
        while (i <= mid && j <= high) {
            if (array[i] <= array[j]) {
                auxiliary_array[index] = array[i];
                ++i;
            } else {
                auxiliary_array[index] = array[j];
                ++j;
            }
            ++index;
        }
        while (i <= mid) {
            auxiliary_array[index] = array[i];
            ++index;
            ++i;
        }
        while (j <= high) {
            auxiliary_array[index] = array[j];
            ++index;
            ++j;
        }
    
        for (int count = 0; count < index; ++count) {
            array[low + count] = auxiliary_array[count];
        }
    }

     非递归:

    void Merge(int* array, int low, int high, int* auxiliary_array)
    {
        int i = low;
        int mid = (low + high) / 2;
        int j = mid + 1;
        int index = 0;
        while (i <= mid && j <= high) {
            if (array[i] <= array[j]) {
                auxiliary_array[index] = array[i];
                ++i;
            } else {
                auxiliary_array[index] = array[j];
                ++j;
            }
            ++index;
        }
        while (i <= mid) {
            auxiliary_array[index] = array[i];
            ++index;
            ++i;
        }
        while (j <= high) {
            auxiliary_array[index] = array[j];
            ++index;
            ++j;
        }
    
        for (int count = 0; count < index; ++count) {
            array[low + count] = auxiliary_array[count];
        }
    }
    
    void MergeSort(int* array, int length)
    {
        int* auxiliary_array = new int[8];
    
        for(int step = 2; step < length; step *= 2) {
            for (int start = 0; start <= length; start += step) {
                int end = start + step - 1;
                if (end - length >= 0) end = length - 1;
                Merge(array, start, end, auxiliary_array);
            }
        }
        Merge(array, 0, length - 1, auxiliary_array);
        delete[] auxiliary_array;
    }

     这里我觉得把Merge函数和MergeSort函数合并太乱了,就没写在一起。虽然这样效率会低点,但是用于学习是不错的。

    二、链表

    递归,原地归并:

    Node* Merge(Node* start1, Node* start2)
    {
        if (nullptr == start1) return start2;
        if (nullptr == start2) return start1;
    
        //select the head from start1 and start2
        Node* head = nullptr;
        if (start1->data <= start2->data) {
            head = start1;
            start1 = start1->next;
        } else {
            head = start2;
            start2 = start2->next;
        }
    
        //merge start1 part and start2 part
        Node* current = head;
        while (nullptr != start1 && nullptr != start2) {
            if (start1->data <= start2->data) {
                current->next = start1;
                start1 = start1->next;
            } else {
                current->next = start2;
                start2 = start2->next;
            }
            current = current->next;
        }
    
        if (nullptr != start1) {
            current->next = start1;
        }
    
        if (nullptr != start2) {
            current->next = start2;
        }
    
        return head;
    }
    
    Node* MergeSort(Node* head)
    {
        if (nullptr == head || nullptr == head->next) return head;
    
        Node* first = head;
        Node* second = first->next;
    
        while(nullptr != second && nullptr != second->next) {
            first = first->next;
            second = second->next->next;
        }
    
        second = first->next;
        first->next = nullptr;
    
        head = Merge(MergeSort(head), MergeSort(second));
    return head; }

     非递归,原地归并:

    Node* Merge(Node* pre, Node* start1, Node* start2, Node* next)
    {//pre is the previous node of start1 and next is the next node of start2
        if (nullptr == start1) return start2;
        if (nullptr == start2) return start1;
        
        //select the head from start1 and start2
        Node* head = nullptr;
        if (start1->data <= start2->data) {
            head = start1;
            start1 = start1->next;
        } else {
            head = start2;
            start2 = start2->next;
        }
    
        //merge start1 part and start2 part
        Node* current = head;
        while (nullptr != start1 && nullptr != start2) {
            if (start1->data <= start2->data) {
                current->next = start1;
                start1 = start1->next;
            } else {
                current->next = start2;
                start2 = start2->next;
            }
            current = current->next;
        }
    
        if (nullptr != start1) {
            current->next = start1;
        }
    
        if (nullptr != start2) {
            current->next = start2;
        }
    
        while (nullptr != current->next) {
            current = current->next;
        }
    
        //reconnect to the origin linkeslist
        pre->next = head;
        current->next = next;
    
        return current;//return the tail of the sorted linkedlist, not the tail of origin linkedlist
    }
    
    Node* MergeSort(Node* head, int length)
    {//length is the length of the linkelist
        if (nullptr == head || nullptr == head->next) return head;
    
        Node* new_head = new Node;
        new_head->next = head;
    
        Node* pre = nullptr;//the previous node of the first part waiting for sorting
        Node* next = nullptr;//the next node of the second part waiting for sorting
    
        //these two noded are used to get the middle node of the linkedlist
        Node* first = nullptr;
        Node* second = nullptr;
    
        for (int step = 2; step < length; step *= 2) {
            pre = new_head;
            while (nullptr != pre && nullptr != pre->next) {
                int index = step / 2 + 1;
                first = pre->next;
                second = first->next;
                while (index < step && nullptr != second && nullptr != second->next) {//get the middle node
                    first = first->next;
                    second = second->next->next;
                    ++index;
                }
    
                if (nullptr != second) {
                    next = second->next;
                    second->next = nullptr;
                } else {
                    next = second;
                }
                second = first->next;
                first->next = nullptr;
    
                pre = Merge(pre, pre->next, second, next);
            }
        }
    
        //merge the entire linkedlist
        first = new_head->next;
        second = first->next;
        while (nullptr != second && nullptr != second->next) {
            first = first->next;
            second = second->next->next;
        }
        second = first->next;
        first->next = nullptr;
        Merge(new_head, new_head->next, second, nullptr);
        head = new_head->next;
        delete new_head;
    
        return head;
    }

           本算法,虽然是原地排序,但是因为是根据步长来迭代的,需要计算链表的长度,要遍历整个数组,即多遍历了一次链表,我觉得与链表的快排相比,没有特别明显的优势。

    而且我的代码写的有点乱,结构不好,如果调整一下,应该能较少20行代码,比如这篇文章——单向链表的原地归并排序实现的写法就比我好。

    下面是一种更加高明的非递归归并排序:

    void Swap(Node*& head1, Node*& head2)
    {
        Node* temp = head1;
        head1 = head2;
        head2 = temp;
    }
    
    Node* Merge(Node* start1, Node* start2)
    {
        if (nullptr == start1) return start2;
        if (nullptr == start2) return start1;
    
        //select the head from start1 and start2
        Node* head = nullptr;
        if (start1->data <= start2->data) {
            head = start1;
            start1 = start1->next;
        } else {
            head = start2;
            start2 = start2->next;
        }
    
        //merge two parts of the linkedlist
        Node* current = head;
        while (nullptr != start1 && nullptr != start2) {
            if (start1->data <= start2->data) {
                current->next = start1;
                start1 = start1->next;
            } else {
                current->next = start2;
                start2 = start2->next;
            }
            current = current->next;
        }
    
        if (nullptr != start1) {
            current->next = start1;
        }
    
        if (nullptr != start2) {
            current->next = start2;
        }
    
        return head;
    }
    
    Node* MergeSort(Node* head)
    {
        if (nullptr == head || nullptr == head->next) return head;
    
        int max_merge_level = 0;
        Node* temp = nullptr;
        Node* merged_linked_list[64] =  { nullptr };
    
        Node* next = nullptr;
        while (nullptr != head) {
            int index = 0;
            next = head->next;
            head->next = temp;
            temp = head;
            head = next;
    
            //merged_linked_list[index] has a capicity of 2 ^ index
            //when the number of nodes merged in merged_linked_list[index] reach 2 ^ (index + 1)
            //these nodes will be placed in the merged_linked_list[index + 1]
            while (index < max_merge_level && nullptr != merged_linked_list[index]) {
                merged_linked_list[index] = Merge(temp, merged_linked_list[index]);
                temp = nullptr;
                Swap(merged_linked_list[index], temp);
                ++index;
            }
            Swap(merged_linked_list[index], temp);
    
            if (index == max_merge_level) ++max_merge_level;
        }
    
        int index = 0;
        while (index <= max_merge_level) {
            merged_linked_list[index + 1] = Merge(merged_linked_list[index],
                                                  merged_linked_list[index + 1]);
            merged_linked_list[index] = nullptr;
            ++index;
        }
    
        return merged_linked_list[index];
    }

         merged_linked_list[64]的作用就是表示已经归并排序了的链表节点,例如merged_linked_list[1]代表有两个节点已经排序,然后此时如果又来了两个排序好的节点,那么就会将这四个节点重新排好序,并赋予merged_linked_list[2],

    依次类推(循环内的代码其实就是这个意思)。

         该算法与上面的算法相比,虽然需要借助辅助空间,但是辅助空间的大小是确定的,而且它有自己的两个优势:

    ①该算法不用在排序之前先遍历一次链表来确定长度,由此来确定最大归并步长。

    ②代码简洁,可能咋一看觉得算法不好懂,但是有注释的话还是清晰易懂的。

  • 相关阅读:
    很简单的企业管理器我写程序的方式,几个自定义控件。
    当OO遇到了持久化?!
    [自定义服务器控件] 第一步:文本框。
    [面向过程——老酒换新瓶] (一)开篇:是面向过程还是面向对象?
    个人理财小助手 —— 设计思路、功能说明
    《Head First 设计模式》 终于出中文版了。
    其实添加数据也可以这样简单——表单的第一步抽象(针对数据访问层)《怪怪设计论: 抽象无处不在 》有感
    基类、接口的应用——表单控件:一次添加、修改一条记录,一次修改多条记录。(上)
    其实添加数据也可以这样简单——表单的第三步抽象(针对UI及后置代码)
    转帖:客户端表单通用验证checkForm(oForm) js版
  • 原文地址:https://www.cnblogs.com/wolves-dave/p/3307999.html
Copyright © 2011-2022 走看看