文章目录
链表题
- 设置两个指针。
- 用栈或队列或hash表来存储相关信息。
一、从尾到头打印链表(1版面试题5)
1.题目
输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
2.思路
要注意算法是否会改变原链表的结构
后进先出首先就想到用栈结构。
3.代码
import java.util.*;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
Stack<Integer> temp = new Stack<>();
ArrayList<Integer> arr = new ArrayList<>();
while(listNode != null){ //把结点依次放入栈中
temp.push(listNode.val);
listNode = listNode.next;
}
while(!(temp.isEmpty())){ //依次弹栈存到目标链表中
arr.add(temp.pop());
}
return arr;
}
}
二、链表中环的入口结点(2版面试题23)
1.题目
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
2.思路
遍历链表,当遇到之前遍历过的结点时(说明有环),返回这个结点。
判断是否遇到遍历过的结点的方法:
将之前遍历的结点以此放入到ArrayList中,然后利用contains函数即可。
3.代码
import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead)
{
ListNode start = null;
ArrayList<ListNode> arr = new ArrayList<>();
while(pHead != null){
if(arr.contains(pHead)){
return pHead;
}
arr.add(pHead);
pHead = pHead.next;
}
return start;
}
}
三、删除链表中的重复结点
1.题目
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
2.算法思路
设置两个结点,一个是上一个不重复的结点pre。一个是寻找下一个不重复的结点last。同时,再创建一个头节点head(好判断pHead是否重复)
3.代码
public class Solution {
public ListNode deleteDuplication(ListNode pHead)
{
if(pHead==null || pHead.next==null){return pHead;}
ListNode head = new ListNode(Integer.MIN_VALUE);
head.next = pHead;
ListNode pre = head; //pre是上一个不重复的结点
ListNode last = pHead; //last是用来寻找下一个不重复的结点
while(last != null){
if(last.next != null && last.val == last.next.val){ //发现last和last.next重复
while(last.next != null && last.val == last.next.val){ //该循环是找到当前一直重复的最后一个结点
last = last.next;
}
pre.next = last.next;
last = last.next;
}else{
pre = pre.next;
last = last.next;
}
}
return head.next; //注意,pHead可能已经改变(有可能出现11112的情况)
}
}
四、链表中倒数第k个结点
1.题目
输入一个链表,输出该链表中倒数第k个结点。
2.思路
(1)利用栈,需要额外的空间
先将链表放入栈中,再弹出第k个结点即可。
(2)设置两个指针p,q
p从开始就走,q比p慢k步。当p走到链表尾时,这是q就处于倒数第k个结点。
3.代码
(1)利用栈,需要额外的空间
import java.util.Stack;
public class Solution {
public ListNode FindKthToTail(ListNode head,int k) {
if(head==null || k<=0){return null;} //特殊情况
Stack<ListNode> sta = new Stack<>();
ListNode temp = head;
while(temp!=null){ //链表放入栈中
sta.push(temp);
temp = temp.next;
}
for(int i=1;i<k;i++){ //先弹出k-1个结点
sta.pop();
}
if(sta.size() == 0){ //若不存在倒数第k个
return null;
}
return sta.pop(); //弹出倒数第k个
}
}
(2)设置两个指针p,q
public class Solution {
public ListNode FindKthToTail(ListNode head,int k) {
ListNode p, q;
p = q = head; //两个指针都从头开始走
int i = 0;
for (; p != null; i++) { //p开始就走,一直走到链表结尾
if (i >= k){ //当p走了k步时,q才开始走
q = q.next;
}
p = p.next; //p开始就走
}
return i < k ? null : q; //当p走到链表尾时,这是q刚好在倒数第k(q比p慢k步)
}
}
五、反转链表
1.题目
输入一个链表,反转链表后,输出新链表的表头。
2.思路
链表的题目先画图。
3.代码
public class Solution {
public ListNode ReverseList(ListNode head) {
ListNode pre = null; //pre用于记录当前结点的前一个结点
ListNode next = null; //next用于记录当前结点的下一个结点
while(head != null){
next = head.next; //用next记录当前结点的下一个结点地址
head.next = pre; //让被当前结点与链表断开并指向前一个结点pre。
pre = head; //pre指针指向当前结点
head = next; //head指向next(保存着原链表中head的下一个结点地址)
}
return pre;//当循环结束时,pre所指的就是反转链表的头结点
}
}
六、两个链表的第一个公共结点
1.题目
输入两个链表,找出它们的第一个公共结点。
2.思路
**公共结点的特征:**有两个前结点,有一个后结点。
(1)利用公共结点特征+栈
用两个栈分别来装这两个链表。然后这两个栈一起弹栈,若相等,则说明使用的同一个结点。第一次遇到不同,说明,上一个结点就是第一个公共结点。
3.代码
(1)利用公共结点特征+栈
import java.util.Stack;
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
Stack<ListNode> sta1 = new Stack<>();
Stack<ListNode> sta2 = new Stack<>();
ListNode result = null;
while(pHead1!=null){ //用栈sta1装链表1
sta1.push(pHead1);
pHead1 = pHead1.next;
}
while(pHead2!=null){ //用栈sta2装链表2
sta2.push(pHead2);
pHead2 = pHead2.next;
}
while(!(sta1.isEmpty() || sta2.isEmpty())){ //两个栈同时弹栈
if(sta1.peek().val == sta2.peek().val){ //若相等,说明是公共结点,并记录这个公共结点
result = sta1.peek();
sta1.pop();
sta2.pop();
}else{ //第一次发现不相等,直接结束循环,此时result保存的就是第一个公共结点
break;
}
}
return result;
}
}
七、合并两个排序的链表
1.题目
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
2.思路
要说明的是,ListNode类是指链表的节点,每个节点都存放着本节点的值和下一节点的地址(对象)
先考虑特殊的情况,如果 l1 或者 l2 一开始就是 null ,那么没有任何操作需要合并,所以我们只需要返回非空链表。否则,我们要判断 l1 和 l2 哪一个的头元素更小,然后递归地决定下一个添加到结果里的值(即相当于从同时从两个链表中找出下一个最小值放在下一节点)。如果两个链表都是空的,那么过程终止。
部分步骤如下,也就是说merge(L1,L2)这个函数相当于找出L1与L2中的最小值
3.代码
public class Solution {
public ListNode Merge(ListNode l1,ListNode l2){
if(l1 == null){
return l2;
}
if(l2 == null){
return l1;
}
if(l1.val < l2.val){ //若L1的头节点值 < L2的头节点值
l1.next = Merge(l1.next,l2); //相当于把L2插入到L1中
return l1;
} else{ //若L1的头节点值 >= L2的头节点值
l2.next = Merge(l1,l2.next); //相当于把L1插入到L2中
return l2;
}
}
}