关于链表的算法题2:反转链表,删除链表中的节点,回文链表,旋转链表,合并两个有序链表,合并K个有序链表

1. 反转链表

反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
两种方法 :1是循环,2是递归

Java答案:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
1. 循环 
class Solution {
    public ListNode reverseList(ListNode head) {
        if(head==null){
            return null;
        }
        ListNode p = head.next;
        head.next=null;
        while(p!=null){
            ListNode q = p;
            p=p.next;
            q.next = head;
            head = q;
        }
        return head;
    }
}
执行时间:1ms,内存消耗:35.6MB
2. 递归:两个节点反转就是head.next.next = head; 最好是紧接着让head.next =null,因为第一个的next是null
class Solution {
    public ListNode reverseList(ListNode head) {
       if (head == null || head.next == null) return head;
       ListNode p = reverseList(head.next);
       head.next.next = head;
       head.next = null;
       return p;
    }
}
执行时间:1ms,内存消耗:35.4MB

javaScript答案:

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
 1. 使用循环头插法
var reverseList = function(head) {
        var root = null;
        var currentNode =head;
        while(currentNode!==null){
            const next = currentNode.next;
            currentNode.next = root;
            root = currentNode;
            currentNode = next;
        }
        return root;
};
执行时间:92ms,内存消耗:34.9MB
2. 使用递归:
var reverseList = function(head) {
    if(head==null || head.next==null){
      return head;
    }
    let h = reverseList(head.next);
    head.next.next = head;
    head.next = null;
    return h;
};
执行时间是92ms,内存消耗:35.7MB
2. 删除链表中的节点

请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。
现有一个链表 – head = [4,5,1,9],它可以表示为:
在这里插入图片描述
示例 1:
输入: head = [4,5,1,9], node = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.

示例 2:
输入: head = [4,5,1,9], node = 1
输出: [4,5,9]
解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.

说明:
链表至少包含两个节点。
链表中所有节点的值都是唯一的。
给定的节点为非末尾节点并且一定是链表中的一个有效节点。
不要从你的函数中返回任何结果。

java答案:

1. 这个题没有告诉链表,所以不能使用平时的删除操作,前一节点的指针指向下一下节点。
class Solution {
    public void deleteNode(ListNode node) {
      if(node==null||node.next==null){
            return;
        }
        else{
            ListNode temp=node.next;
            node.val=temp.val;
            node.next=temp.next;
            temp=null;
        }
    }
}
2. 因为题目中给出了node不是末尾的节点
class Solution {
    public void deleteNode(ListNode node) {
        node.val = node.next.val;
        node.next = node.next.next;
    }
}

JavaScript答案:

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} node
 * @return {void} Do not return anything, modify node in-place instead.
 */
var deleteNode = function(node) {
    node.val = node.next.val;
    node.next =node.next.next;
};
3. 回文链表

请判断一个链表是否为回文链表。
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
两种方法 :1是栈,而是快慢指针,找到中间位置,然后要么反转后边的链表部分比较,要么从中间向两边比较。
java答案:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
 1. 使用栈的先进后出特点,比较值是否相等
class Solution {
    public boolean isPalindrome(ListNode head) {
        if(head==null || head.next==null){
            return true;
        }
        ListNode p = head;
        Stack<ListNode> s = new Stack<>();
        while(p!=null){
            s.push(p);
            p = p.next;
        }
        while(!s.isEmpty()){
            if(s.peek().val != head.val){
                return false;
            }
            s.pop();
            head=head.next;
        }
        return true;
    }
}
执行时间:12ms,内存消耗:45.1MB
2.
/*
 * @lc app=leetcode.cn id=234 lang=java
 *
 * [234] 回文链表
 */
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isPalindrome(ListNode head) {
        // 排除异常情况: 链表为空或只有一位时 返回真
        if (head == null || head.next == null) {
            return true;
        }
        // 排除异常情况: 链表只有两位时 直接判断这两位是否相等
        if (head.next.next == null) {
            return head.val == head.next.val;
        }
        // 声明快慢指针
        ListNode slow = head;
        ListNode fast = head;
        // 声明last指针 用以记录循环的慢指针上一次遍历的节点
        // 在遍历时转向前半部分链表
        ListNode last = null;
        // 用以记录遍历过程的临时值
        ListNode tmp = null;
        while (fast.next != null && fast.next.next != null) {
            // 记录下当前遍历的节点 等下赋值给last指针
            tmp = slow;
            slow = slow.next;
            fast = fast.next.next;
            // 反转链表
            if (last == null) {
                // 第一次循环 last指针指向头部 尾部置空(变头为尾)
                last = head;
                last.next = null;
            } else {
                // 后续循环 将last指针赋值给当前遍历节点的下一位
                tmp.next = last;
                // last指针更新为当前遍历节点
                last = tmp;
            }
        }
        // 上述遍历结束 找到中间值 fast指针使命结束
        // 接下来重新定义快慢指针使命
        // 慢指针: 往前遍历; 快指针: 往后遍历
        if (fast.next == null) {
            // 如果是奇数个 slow此时位于中间数位置
            // fast需要向后一位
            // slow需要向前一位 即当前tmp值
            fast = slow.next;
            slow = tmp;
        } else {
            // 如果是偶数个 slow此时位于前半部分最后一位数位置
            // fast需要向后一位
            // slow不需要移动 但是需要补充指针转向操作
            fast = slow.next;
            slow.next = tmp;
        }
        // 遍历判断
        while (slow != null || fast != null) {
            if (slow == null || fast == null 
                || slow.val != fast.val) {
                return false;
            }
            slow = slow.next;
            fast = fast.next;
        }
        return true;
    }
}
执行时间:2ms,内存消耗:44.7MB

JavaScript答案:

1. 只要把上面第2种变量类型改成let即可。
执行时间是104ms,内存消耗是38.8MB
2. /**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @return {boolean}
 */
var isPalindrome = function(head) {
    if(!head) return true;
    let s = head;
    let f = head;

    while(f && f.next && f.next.next) {
        s = s.next;
        f = f.next.next;
    }
    let right = s.next;
    s.next = null;

    let current = right;
    let last = null;
    //后半部分链表反转
    while(current){
        const next = current.next;
        current.next = last;
        last = current;
        current = next;
    }
    a = head;
    b = last;
    while(a && b) {
        if(a.val !== b.val) {
            return false;
        }
        a = a.next;
        b = b.next;
    }
    return true;
};
执行时间是92ms,内存消耗是39.4MB
4. 旋转链表

给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。

示例 1:
输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
向右旋转 1 步: 5->1->2->3->4->NULL
向右旋转 2 步: 4->5->1->2->3->NULL

示例 2:
输入: 0->1->2->NULL, k = 4
输出: 2->0->1->NULL
解释:
向右旋转 1 步: 2->0->1->NULL
向右旋转 2 步: 1->2->0->NULL
向右旋转 3 步: 0->1->2->NULL
向右旋转 4 步: 2->0->1->NULL

java答案:

1. 先算出链表的长度,判断和k的关系,使用一个链表,找到右移k个之后的头节点,然后把尾部连到head上
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode rotateRight(ListNode head, int k) {
        if(head==null || head.next==null||k==0)
            return head;
        ListNode p = head;
        int count=0;
        while(p!=null){
            count++;
            p=p.next;
        }
        //System.out.println(count);
        if(count<=k){
            k = k%count;
        }
        if(k==0){
            return head;
        }
       // System.out.println("k"+k);
        p=head;
        k=count-k;
       // System.out.println("k1:"+k);
        while(k!=1){
            p=p.next;
            k--;
        }
        ListNode q =p.next;
        ListNode m =p.next;
        p.next=null;
        while(q.next!=null){
            q=q.next;
        }
      //  System.out.println("q"+q.val);
        q.next=head;
        return m;
    }
}

JavaScript答案:

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} k
 * @return {ListNode}
 */
var rotateRight = function(head, k) {
     if(head==null || head.next==null||k==0)
            return head;
        var p = head;
        var count=0;
        while(p!=null){
            count++;
            p=p.next;
        }
        if(count<=k){
            k = k%count;
        }
        if(k==0){
            return head;
        }
        p=head;
        k=count-k;
        while(k!=1){
            p=p.next;
            k--;
        }
        var q =p.next;
        var m =p.next;
        p.next=null;
        while(q.next!=null){
            q=q.next;
        }
        q.next=head;
        return m;
};
5. 合并两个有序链表

将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
两种方法 :1.循环迭代,2递归
Java答案:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
 1.循环迭代:
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if(l1==null){
            return l2;
        }
        if(l2==null){
            return l1;
        }
        ListNode p = l1;
        ListNode q = l2;
        ListNode l3 = null;
        ListNode m = null;
        while(p!=null && q!=null){
            if(p.val<=q.val){
                if(l3==null){
                    l3=p;
                    m=p;
                    p=p.next;
                }else{
                    l3.next = p;
                    p=p.next;
                    l3=l3.next;
                }
            }else{
                if(l3==null){
                    l3=q;
                    m=q;
                    q=q.next;
                }else{
                    l3.next = q;
                    q=q.next;
                    l3=l3.next;
                }
            }
        }
        if(p!=null){
            l3.next = p;
        }
        if(q!=null){
            l3.next=q;
        }
        return m;
    }
}
执行时间是2ms,内存消耗是35.6MB
时间复杂度是o(m+n),空间复杂度是o(1)
2. 递归
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if(l1==null){
            return l2;
        }
        if(l2==null){
            return l1;
        }
        if (l1.val < l2.val) {
            l1.next = mergeTwoLists(l1.next, l2);
            return l1;
        }else {
            l2.next = mergeTwoLists(l1, l2.next);
            return l2;
        }
    }
    执行时间是2ms,内存消耗是35.1MB
    时间复杂度是o(m+n),空间复杂度:o(m+n):m+n个栈帧会消耗o(m+n)的空间。

JavaScript答案:

1. 上面1方法执行时间92ms,内存消耗是35.3MB
2. 上面2方法执行时间100ms,内存消耗是34.7MB
6. 合并k个排序链表

合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
示例:
输入:
[
1->4->5,
1->3->4,
2->6
]
输出: 1->1->2->3->4->4->5->6

Java答案:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
 1. 每次合并两个,调用之前写的两链表合并的代码,直到lists中只剩1个链表,并返回这个链表。
 class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if (lists.length == 0)
            return null;
        int k = lists.length;
        while (k > 1) {            
            for (int i = 0; i < k / 2; i++) 
                lists[i] = mergeTwoLists(lists[i], lists[i + (k + 1) / 2]); //在中间分开,每次左边数组的第一个链表和右边数组的第一个链表合并
            k = (k + 1) / 2; //k+1是因为可能存在奇数,多出来一个单的
        }
        return lists[0];
    }
     //两个链表合并
    private ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode root = new ListNode(0);
        ListNode node = root;
        while (list1 != null && list2 != null) {
            if (list1.val <= list2.val) {
                node.next = list1;
                list1 = list1.next;                
            } else {
                node.next = list2;
                list2 = list2.next;                  
            }
            node = node.next;
        }
        if (list1 == null)
            node.next = list2;
        if (list2 == null)
            node.next = list1;
        return root.next;        
    }
}
执行时间是5ms,内存消耗是39.6MB

 2.逐一比较,初始化最小值是Integer中最大的值,查找最小的值,找到接在最终有序链表的后面,如果最后所有链表都为null时,返回。
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
          ListNode sumAll = new ListNode(0); //最终有序链表的表头
        ListNode p = sumAll;
        boolean flag = true; //为了控制while循环的
        while(flag){
          int min = Integer.MAX_VALUE; // 写在while里是因为每次找到最小值以后,需要重新设置min为 Integer.MAX_VALUE
           int idx = -1;
          for(int i=0;i<lists.length;i++){
            if(lists[i]!=null && lists[i].val < min){ //需要先判断lists[i]是否为空,否则报错
                min = lists[i].val;
                idx = i;
            }
          }   
          if(idx!=-1){ //找到最小值,接在最终链表后面
              p.next = lists[idx];
              p=p.next;
              lists[idx] = lists[idx].next;
          }else{ //如果idx是-1,代表所有链表为空了,结束while循环
              flag = false;
          }
        }
        return sumAll.next;
    }
}
执行时间是334ms,内存消耗是40.7MB 
3.  优先级队列
/**
     * 优先级队列
     * @param lists
     * @return
     */
class Solution{
   public ListNode mergeKLists(ListNode[] lists) {
        if(lists == null || lists.length == 0){
            return null;
        }
        PriorityQueue<ListNode> queue = new PriorityQueue<ListNode>(new Comparator<ListNode>() {
            @Override
            public int compare(ListNode o1, ListNode o2) {
                return o1.val - o2.val;
            }
        });
        for(int i = 0; i < lists.length; i++){
            if(lists[i] != null){
                queue.offer(lists[i]);
            }
        }
        ListNode head = null;
        ListNode cur = null;
        while (!queue.isEmpty()){
            ListNode node = queue.poll();
            if(head == null){
                head = node;
                cur = head;
            }else {
                cur.next = node;
                cur = cur.next;
            }
            if(node.next != null){
                queue.offer(node.next);
            }
        }
        return head;
    }   
}
执行时间是14ms,内存消耗是42.7MB

JavaScript答案:

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode[]} lists
 * @return {ListNode}
 */
 1. 用的上面两两归并的方法,但是注意整数相除
var mergeKLists = function(lists) {
    if(lists.length==0){
       return null;
    }
    let k = lists.length;
    while(k>1){
        for(let i=0;i<Math.floor(k/2);i++){
          lists[i] = merget(lists[i],lists[i+Math.floor((k+1)/2)]);
        }   
        k = Math.floor((k+1)/2);
    }
    return lists[0];
};
var merget = function(list1,list2){
        if(list1==null){
           return list2;
        }
        if(list2==null){
           return list1;
        }
        let root = new ListNode(0);
        let node = root;
        while (list1 != null && list2 != null) {
            if (list1.val <= list2.val) {
                node.next = list1;
                list1 = list1.next;                
            } else {
                node.next = list2;
                list2 = list2.next;                  
            }
            node = node.next;
        }
        if (list1 == null)
            node.next = list2;
        if (list2 == null)
            node.next = list1;
        return root.next;     
};
执行时间是128ms,内存消耗是39.1MB 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值