合并两个无序链表

楼主昨天看到阿里的一道算法题是只使用O(1)的额外空间,合并两个无序链表。乍一看有点奇怪,一般都是双指针算法合并两个有序链表,后面思考过后除了不能像数组能够随机访问链表的每个元素外合并两个有序链表实际上就是先对每个链表进行排序,然后将两个有序链表合并起来。也就是分为两步。

  1. 对两个链表分别排序
  2. 将两个有序链表合并起来
    然后楼主看了一下网上的其他帖子,大部分都是使用选择啊、冒泡啊这种O(n^2)的算法。这效率实在太低了。尽管链表确实不像数组那样访问元素方便,但是通过快慢指针我们页能很快的就找到我们链表中指定的某个元素。而且链表天生就支持类似归并排序的操作,因此下面给出一种类似于归并排序O(nlogn)的排序算法;
public class 合并两个无序链表 {
    static Node l1;
    static Node l2;
    static class Node {
        int val;
        Node next;

        public Node() {
        }

        public Node(int val) {
            this.val = val;
        }

        public Node(int val, Node next) {
            this.val = val;
            this.next = next;
        }
    }
    private static void init() {
        Node vir1 = new Node();
        Node vir2 = new Node();
        Node p = vir1;
        Node q = vir2;
        Random random = new Random();
        for (int i = 0; i < 3; i++) {
            Node np = new Node(random.nextInt(30));
            p.next = np;
            p = np;
            Node nq = new Node(random.nextInt(30));
            q.next = nq;
            q = nq;
        }
        l1 = vir1.next;
        l2 = vir2.next;
    }

    private static void show(Node l1,Node l2){
        Node p = l1;
        Node q = l2;
        while (p != null){
            System.out.print(p.val+" ");
            p = p.next;
        }
        System.out.println();
        while (q != null){
            System.out.print(q.val + " ");
            q = q.next;
        }
        System.out.println();
    }


    private static Node merge(Node h1, Node h2) {
//        System.out.println("merge");
        //合并两个有序链表
        Node vir = new Node();
        Node sc =vir;
        Node p = h1;
        Node q = h2;
        while (p != null && q != null){
            if (p.val <= q.val){
                Node pn = p.next;
                sc.next = p;
                p = pn;
            }else {
                Node qn = q.next;
                sc.next = q;
                q = qn;
            }
            sc = sc.next;
//            sc.next = null;
        }
        if (p != null) sc.next = p;
        if (q != null) sc.next = q;
        return vir.next;
    }

    private static Node merge_sort(Node head) {
//        System.out.println("merge_sort");
        if (head == null || head.next == null) return head;
        Node fast = head;
        Node low = new Node(0,head);
        // 1 2
        while (fast != null && fast.next != null){
            fast = fast.next.next;
            low = low.next;
        }
        //low所在的位置是右边头节点的前一个
        Node rhead = low.next;
        low.next = null;//断开连接
        Node lh = merge_sort(head);
        Node rh = merge_sort(rhead);
        Node merge = merge(lh, rh);
        return merge;
    }



    public static void main(String[] args) {
        init();
        show(l1,l2);
        l1 = merge_sort(l1);
        l2 = merge_sort(l2);
        Node res = merge(l1,l2);
        show(res,null);
    }

}

上述Node为链表节点、init()方法是对链表进行初始化,生成测试数据(因为楼主找了半天也没找到一个OJ上有这道题),show()是打印链表的内容。剩下就是核心方法merge_sort()和merge()方法。
merge_sort()实际上就是通过快慢指针,快速定位到链表的中点、然后对链表进行分割,分为两个小链表进行排序,最终当链表为一个节点的时候则问题解决,这是分治的思路。
merge()没什么好说的就是合并两个有序链表.
然后我们来看看算法时间复杂度:对于每个merge_sort的方法,快慢指针需要O(n)的时间复杂度,合并两个有序链表也是O(n)的复杂度,然后最多调用logn层的merge_sort,所以最终的算法时间复杂度为O(nlogn)。

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是使用C语言合并两个无序链表的示例代码: ```c #include <stdio.h> #include <stdlib.h> typedef struct node { int data; struct node *next; } Node; Node* createList(int arr[], int n) { Node *head = NULL, *tail = NULL; for (int i = 0; i < n; i++) { Node *newNode = (Node*) malloc(sizeof(Node)); newNode->data = arr[i]; newNode->next = NULL; if (tail == NULL) { head = tail = newNode; } else { tail->next = newNode; tail = newNode; } } return head; } void printList(Node *head) { while (head != NULL) { printf("%d ", head->data); head = head->next; } printf("\n"); } Node* mergeLists(Node *head1, Node *head2) { if (head1 == NULL) { return head2; } if (head2 == NULL) { return head1; } Node *ptr1 = head1, *ptr2 = head2; Node *head = NULL, *tail = NULL; while (ptr1 != NULL && ptr2 != NULL) { Node *newNode = (Node*) malloc(sizeof(Node)); if (ptr1->data <= ptr2->data) { newNode->data = ptr1->data; ptr1 = ptr1->next; } else { newNode->data = ptr2->data; ptr2 = ptr2->next; } newNode->next = NULL; if (tail == NULL) { head = tail = newNode; } else { tail->next = newNode; tail = newNode; } } if (ptr1 != NULL) { tail->next = ptr1; } if (ptr2 != NULL) { tail->next = ptr2; } return head; } int main() { int arr1[] = {3, 1, 4, 5, 2}; int arr2[] = {9, 6, 7, 8}; Node *head1 = createList(arr1, 5); Node *head2 = createList(arr2, 4); printf("List 1: "); printList(head1); printf("List 2: "); printList(head2); Node *head = mergeLists(head1, head2); printf("Merged List: "); printList(head); return 0; } ``` 在上面的代码中,我们定义了一个`Node`结构体来表示链表的节点,其中包含一个整数数据和一个指向下一个节点的指针。我们还定义了`createList`函数来创建链表,`printList`函数来打印链表,以及`mergeLists`函数来合并两个链表。 `mergeLists`函数使用两个指针`ptr1`和`ptr2`分别遍历两个链表,并将它们的节点逐个比较,将较小的节点添加到新链表中。最后,将任何剩余的节点添加到新链表的末尾,并返回新链表的头指针。 在`main`函数中,我们创建了两个无序链表,打印它们,然后将它们合并并打印结果链表。运行程序将输出以下内容: ``` List 1: 3 1 4 5 2 List 2: 9 6 7 8 Merged List: 1 2 3 4 5 6 7 8 9 ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值