Data structures 如何合并已排序列表的两个半部分

Data structures 如何合并已排序列表的两个半部分,data-structures,linked-list,Data Structures,Linked List,我有一个整数链表,它的前半部分和后半部分是独立排序的。现在我需要合并两部分来创建一个单独的排序链表 样本输入: 输入列表1:1->2->3->4->5->1->2 输出:1->1->2->2->3->4->5 输入列表2:1->5->7->9->11->2->4->6 输出2:1->2->4->5->6->7->9->11 预期输出: 1,2,3….这被称为 查找并查找实现细节。查找自然合并排序以获得最佳解决方案:这是合并排序 我将保留两个指针: ptr1指向前半部分的第一个元素 并且ptr2指

我有一个整数链表,它的前半部分和后半部分是独立排序的。现在我需要合并两部分来创建一个单独的排序链表

样本输入:

输入列表1:
1->2->3->4->5->1->2

输出:
1->1->2->2->3->4->5

输入列表2:
1->5->7->9->11->2->4->6

输出2:
1->2->4->5->6->7->9->11

预期输出:

1,2,3….

这被称为


查找并查找实现细节。

查找自然合并排序以获得最佳解决方案:

这是合并排序

我将保留两个指针:

ptr1
指向前半部分的第一个元素

并且
ptr2
指向下半部分的第一个元素

您将需要一个额外的数组来存储最终列表,当然您可以选择不使用这个额外的数组,但是讨论离主题还很远

1,比较
*ptr1
*ptr2
,如果
*ptr1
的值小于
*ptr2
,则将该值(即
*ptr1
)复制到最终数组中,并让
ptr1
向前移动

如果
ptr2
的值较小,只需复制
*ptr2
并让
ptr2
向前移动即可

2,当指针指向最后一个元素后停止,如果前半部分中有5个元素
a[0]a[1]a[2]a[3]a[4]
,则当指针指向
a[5]


3,如果前半部分为空,则复制后半部分的其余部分,反之亦然。

在使用函数式语言时,我已经做了无数次了,因此我想我可以给您一个简短的解释

如果你把它看作是一个递归过程,你会更容易理解它,但我会给你一个命令式的过程。 您可以从部分列表和空的排序列表开始

然后开始循环,您的部分列表如下所示:

  • 都为空,在这种情况下,您的处理完成
  • 其中一个为空,将另一个附加到排序列表中
  • 两个head元素和列表的尾部,您将比较两个head,将最小的添加到排序列表中,将另一个保留在其原始位置,然后循环使用剩余的列表
一旦退出循环,您只需返回已排序的列表

不过有一个警告:如果您使用的是简单的链表,那么您可以通过在排序后的列表前面添加元素,并在返回列表之前将其反转,从而获得更好的运行时间。

int[]merge(int[]left,int[]right){
int[] merge(int[] left, int[] right) {
        int[] ret = new int[left.length + right.length];

        int i = 0;
        int j = 0;
        for (; i < left.length & j < right.length;) {
            if (left[i] < right[j]) {
                ret[i + j] = left[i++];
            } else if (left[i] > right[j]) {
                ret[i + j] = right[j++];
            } else {
                ret[i + j] = left[i];
                ret[i + j + 1] = left[i];
                i++;
                j++;
            }
        }

        if (i < left.length) {

            copyIntArray(left, i, left.length, ret, ret.length - i);
        } else if (j < right.length) {

            copyIntArray(right, j, right.length, ret, ret.length - j);
        }

        return ret;
    }

    void copyIntArray(int[] src, int startIndex, int endIndex, int[] des, int desStartIndex) {
        for (int i = startIndex; i < endIndex; i++) {
            des[desStartIndex++] = src[i];
        }
    }
int[]ret=新int[left.length+right.length]; int i=0; int j=0; 对于(;i右[j]){ ret[i+j]=右[j++]; }否则{ ret[i+j]=左[i]; ret[i+j+1]=左[i]; i++; j++; } } 如果(i<左.长度){ 复制阵列(左,i,左.长度,反向,反向长度-i); }else if(j<右.长度){ 复制阵列(右,j,右.长度,反向,反向长度-j); } 返回ret; } 无效复制阵列(int[]src、int startIndex、int endIndex、int[]des、int desStartIndex){ 对于(int i=startIndex;i
预期的最终输出是
1,2,3
?为什么会这样?简单地说,这与对最初未排序的列表进行排序是一样的,对于这些列表,有很多种。您的链表实现是否提供了迭代器接口和
begin()
end()
样式的函数?在这种情况下,您可以使用
std::merge
算法:@billz预期的输出仍然不是很清楚。我假设它是
1,1,1,2,2,3,4,4,5,5,6,7,9,11
。但可能是
1,2,4,5
,在这种情况下,我们需要一个求交算法。@jogojapan这是两个独立的测试用例。每一个在一个链表中都有两个已排序的组。因此,对于测试用例1,输出为
1,1,2,2,3,4,5
,对于测试用例2,输出为
1,2,4,5,6,7,9,11
。因此,这是合并排序,而不是设置交集。特定的步骤称为合并,它将两个已排序的列表/数组合并到O(n)中。他可能不需要新的名单。他说他有一个大的列表,上半部分和下半部分是排序的,我说的是两个分开的列表的步骤,这就是重点。如果两者都是实际的链接列表(而不是数组),那就没关系了。该死,在智能手机上输入这些内容太糟糕了。:)这是因为你不能只递归地键入第一个字符,然后再键入其他字符;您需要具有显式回溯的算法。:)