归并排序

归并排序,典型的分治思想,时间复杂度为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],

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

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

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

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

posted @ 2013-09-08 00:22  Wolves_群狼  阅读(175)  评论(0编辑  收藏  举报