剑指offer之链表

链表的定义:

public class ListNode {
	int val;
	ListNode next = null;
ListNode(int val) {
	this.val = val;
}
}
  1. 输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。

    分析: 本题利用递归调用实现,还可以利用栈先入后出分特性完成,也可以利用Collections.reverse(list)方法直接将list反转,这里不再赘述。

    import java.util.ArrayList;
    public class Solution {
        ArrayList<Integer> array = new ArrayList<Integer>();
        public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
            if (listNode != null) {
                this.printListFromTailToHead(listNode.next);
                array.add(listNode.val);
                }
                return array;
            }
    }
    
  2. 输入一个链表,输出该链表中倒数第k个结点。

    分析: 本题利用两个指针,实现一个长度为k的测量尺,当第一个指针到达链表结尾时,第二个指针正好位于倒数第k个节点。

    public class Solution {
        public ListNode FindKthToTail(ListNode head,int k) {
            ListNode h = head;
            ListNode t = head;
            int i = 0;
            for (; h != null; i++) {
                if (i >= k) {
                    t = t.next;
                }
                h = h.next;
            }
            return i >= k ? t : null;
        }
    }
    

    注意: 当链表总节点数小于k时,返回null。

  3. 输入一个链表,反转链表后,输出新链表的表头。
    分析: 本体利用两个指针pre 指向当前节点的前一个节点,next指向当前节点的下一个节点,实现链表的反转。
    方法一非递归方法
    实现步骤如下:
    步骤1、head→next1→next2
    步骤2、next = head.next; 实现next→next1→next2,next用来保存head之后的节点
    步骤3、head.next = pre; 实现head→pre,完成链表的反转
    步骤4、最后head和pre分别向后移动一个节点重复上述过程,直到head == null;

    public class Solution {
        public ListNode ReverseList(ListNode head) {
            ListNode pre = null;
            ListNode next = null;
            if (head == null) {
                return null;
            }
            while (head != null) {
                next = head.next;
                head.next = pre;
                pre = head;
                head = next;
            }
            return pre;
        }
    }
    

    方法二递归方法

    首先利用递归到达最后一个节点,将其赋给pre,pre一直不变,然后执行

    head.next.next = head
    head.next = null;
    

    这两句语句,此时head为倒数第二个节点,head.next.next = head实现将倒数第一个节点指向倒数第二个节点,然后第二个节点指向空。下一次递归实现将倒数第二个节点指向倒数三个节点直到递归结束,这个过程中pre一直是指向原链表的最后一个节点,最后返回pre就实现了整个链表的反转。

    疑问 : 为什么链表在这个过程中没有断裂?
    在上一次递归中,前一个节点的next == null, 下一次递归中的节点为什么还能使上一个节点指向本次递归中的节点,不理解。

    public class Solution {
        public ListNode ReverseList(ListNode head) { 
            if (head == null || head.next == null) {
                return head;
            }
            ListNode pre = ReverseList(head.next);
            head.next.next = head;
            head.next = null;
            return pre;
        }
    }
    
  4. 输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
    分析: 利用两个指针,一个头指针,头指针确定后不再移动,current指针随循环移动,最后返回头指针。从头开始分别比较两个链表中元素的值,取出较小的元素放在新链表中,然后后移,重复。

    public class Solution {
        public ListNode Merge(ListNode list1,ListNode list2) {
            
            if (list1 == null) {
                return list2;
            }
            if (list2 == null) {
                return list1;
            }
            ListNode mergeHead = null;
            ListNode current = null;
            while ((list1 != null) && (list2 != null)) {
                if (list1.val <= list2.val) {
                    if (mergeHead == null) {
                        mergeHead = list1;
                        current = list1;
                    } else {
                        current.next = list1;
                        current = current.next;
                    }
                    list1 = list1.next;
                } else {
                    if (mergeHead == null) {
                        mergeHead = list2;
                        current = list2;
                    } else {
                        current.next = list2;
                        current = current.next;
                    }
                    list2 = list2.next;
                }
            }
            if (list1 == null) {
                    current.next = list2;
                }
            if (list2 == null) {
                    current.next = list1;
                }
            return mergeHead;
        }
    }
    
  5. 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)**
    分析: 分三步来实现

    首先,复制链表,将复制后的链表元素依次放在原链表元素的后面,不考虑特殊指针。
    在这里插入图片描述
    实现代码如下:

    RandomListNode currentNode = pHead;
            while (currentNode != null) {
                RandomListNode cloneNode = new RandomListNode(currentNode.label);
                RandomListNode nextNode = currentNode.next;
                currentNode.next = cloneNode;
                cloneNode.next = nextNode;
                currentNode = nextNode;
            }
    

    然后,复制特殊指针。
    在这里插入图片描述
    实现代码如下:

    currentNode = pHead;
            while (currentNode != null) {
                if (currentNode.random != null){
                    currentNode.next.random = currentNode.random.next;
                }
                currentNode = currentNode.next.next;
            }
    

    最后,将两个链表分开。
    在这里插入图片描述
    实现代码如下:

    currentNode = pHead;
            RandomListNode cloneHead = pHead.next;
            while (currentNode != null) {
                RandomListNode cloneNode = currentNode.next;
                currentNode.next = cloneNode.next;
                if (cloneNode.next != null){
                    cloneNode.next = cloneNode.next.next;
                }
                
                currentNode = currentNode.next;
            }
    

    整体代码如下:

    public class Solution {
        public RandomListNode Clone(RandomListNode pHead){
            if (pHead == null) {
                return null;
            }
            
            RandomListNode currentNode = pHead;
            while (currentNode != null) {
                RandomListNode cloneNode = new RandomListNode(currentNode.label);
                RandomListNode nextNode = currentNode.next;
                currentNode.next = cloneNode;
                cloneNode.next = nextNode;
                currentNode = nextNode;
            }
            currentNode = pHead;
            while (currentNode != null) {
                if (currentNode.random != null){
                    currentNode.next.random = currentNode.random.next;
                }
                currentNode = currentNode.next.next;
            }
            
            currentNode = pHead;
            RandomListNode cloneHead = pHead.next;
            while (currentNode != null) {
                RandomListNode cloneNode = currentNode.next;
                currentNode.next = cloneNode.next;
                if (cloneNode.next != null){
                    cloneNode.next = cloneNode.next.next;
                }
                
                currentNode = currentNode.next;
            }
            return cloneHead;
        }
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值