6.从尾到头打印链表
题目描述: 输入一个链表的头节点,从尾到头反过来打印出每个节点的值, 链表的结点定义如下:
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
借助于栈:我们从头到尾遍历链表,可以把遍历的结果放入栈中(先进后出),每经过一个节点,把该节点放入栈中,这样输出栈就实现了从尾到头输出链表元素
//返回动态数组ArrayList
import java.util.*;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
Stack<Integer> stack = new Stack<>();
while (listNode != null) { //一直指向最后一个节点
stack.push(listNode.val); //进栈,取当前节点的值放入栈中
listNode = listNode.next; // 更新当前节点为下一个节点
}
ArrayList<Integer> res = new ArrayList<>();
while (!stack.empty()) {
res.add(stack.pop()); //出栈,取出当前栈顶元素然后放入list中
}
return res;
}
}
//返回数组
class Solution {
public int[] reversePrint(ListNode head) {
Stack<Integer> stack=new Stack<>();
ListNode cur=head;
while(cur!=null){
stack.push(cur.val);
cur=cur.next;
}
int[] arr=new int[stack.size()]; //声明数组
while(!stack.isEmpty()){
for(int i=0;i<arr.length;i++){
arr[i]=stack.pop();
}
}
return arr;
}
}
stack.push( i );是将一个元素i值入栈
stack.empty( );是判断栈中是否为空
stack.pop( );是将栈中当前元素出栈
List集合常用方法之
list.add(e); 向列表的尾部追加指定的元素
利用递归,即每访问到一个结点的时候,先递归输出它后面的结点,再输出该结点自身,这样链表的结果就反过来了。
import java.*;
public class Solution {
ArrayList<Integer> arrayList=new ArrayList<Integer>();
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
if(listNode!=null){ //当前节点不为空
this.printListFromTailToHead(listNode.next); // 往下递归
arrayList.add(listNode.val); //将节点的val值放入arrayList列表中
}
return arrayList;
}
}
22.链表中倒数第k个结点
题目描述:
输入一个链表,输出该链表中倒数第k个结点
通过初始化两个移动节点的位置距离为k,然后同时移动两个节点,知道第二个节点移动到链表的末尾时,移动节点1的位置就是链表倒数第k个节点。
/*****
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*****/
public class Solution {
public ListNode FindKthToTail(ListNode head,int k) {
ListNode removeNode = head;
while (k != 0) {
if (removeNode == null) { //k 大于链表的长度,直接返回null
(removeNode都为null了k还不为0,说明k大于链表长度
)
return null;
}
removeNode = removeNode.next;
k--;
}
while (removeNode != null) { // 这个循环其实就是同时移动head和removeNode两个节点。
removeNode = removeNode.next;
head = head.next;
}
return head;
}
采用递归的方式去模拟链表从尾到头的这样一个方向,然后在从尾到头的过程中,去判断当前节点的位置,是否为倒数第k个即可。
public class Solution {
private ListNode ans; /// 最终返回的结果
private int sum; /// 用来记录当前节点是倒数第几个节点
private void dfs(ListNode node, int k) {
if (node.next != null) {
dfs(node.next, k); /// 继续递归到下一节点。
}
// 下面这部分其实就是判断当前层的节点是倒数第几个节点。
sum++;
if (sum == k) {
ans = node;
}
}
public ListNode FindKthToTail(ListNode head, int k) {
ans = null;
sum = 0;
if (head == null) { /// 说明链表为null,就没有必要去递归的需要了
return null;
}
dfs(head, k); /// 递归遍历链表
return ans;
}
}
23.链表中环的入口结点
题目描述:
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null
通过Map结构保存链表中每个节点出现的次数,第一次出现两次的节点就是我们所要找的环的入口结点,如果没有找到,返回null
/*****
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*****/
import java.util.*;
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead){
Map<ListNode,Integer> map=new HashMap<>();
while(pHead!=null){
map.put(pHead,map.getOrDefault(pHead,0)+1);
if(map.get(pHead)==2){
return pHead;
}
pHead=pHead.next;
}
return null;
}
}
23.反转链表
题目描述: 定义一个函数,输入一个链表的头节点,反转该链表后,输出新链表的头节点。
通过栈的特性去模拟反转的过程
import java.util.*;
public class Solution {
public ListNode ReverseList(ListNode head) {
if (head == null) {
return null;
}
Stack<ListNode> stack = new Stack<>();
while (head != null) {
stack.push(head);
head = head.next;
}
ListNode removeNode = stack.pop(); // 创建新的链表,需要创建一个新的引用
ListNode ans = removeNode;
removeNode.next = null; /// 初始化
while (!stack.isEmpty()) {
ListNode x = stack.pop(); /// 取出栈顶节点元素,然后初始化节点元素的next值
x.next = null;
// 可以用链表的尾接法去理解
removeNode.next = x; //将新节点连接到链表的尾部
removeNode = x; //把新节点更新为链表的尾节点
}
return ans;
}
}
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre=null; //
ListNode cur=head; //初始设定
while(cur!=null){ //终止条件
//头插法三步走
//第一步,暂存
ListNode temp=cur.next;
//第二步,指向
cur.next=pre;
//第三步,归位(先归pre,再cur)
pre=cur;
cur=temp;
}return pre;
}
}
52.两个链表的第一个公共结点
**题目描述:**输入两个链表,找出它们的第一个公共结点
通过栈去模拟从链表的尾部往前遍历两个链表的重合的部分,找到最左侧重合点即可
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
Stack<ListNode> stack1 = new Stack<>();
Stack<ListNode> stack2 = new Stack<>();
while (pHead1 != null) {
stack1.add(pHead1);
pHead1 = pHead1.next;
}
while (pHead2 != null) {
stack2.add(pHead2);
pHead2 = pHead2.next;
}
ListNode ans = null;
while (!stack1.isEmpty() && !stack2.isEmpty()) {
if(stack1.peek().val == stack2.peek().val) { //二者顶部元素值相同,处于相同节点
ans = stack1.peek();
stack1.pop();
stack2.pop();
} else {
break;
}
}
return ans;
}
}
stack.peek() 返回栈顶元素,但不在原先堆栈中删除它
stack.pop() 返回栈顶元素,并在原先堆栈中删除它
先去判断两个链表的长度,移动其中一个链表的头节点,使其两个链表的长度一样,最后从两个链表的头部开始遍历,找到第一个重合点即可。
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
int len1 = 0;
int len2 = 0;
ListNode removeNode1 = pHead1;//统计长度一定要新建变量,
ListNode removeNode2 = pHead2;
while (removeNode1 != null) {
len1++;
removeNode1 = removeNode1.next;
}
while (removeNode2 != null) {
len2++;
removeNode2 = removeNode2.next;
}
// 下面的两个判断就是是的两个链表的length相同
if (len1 > len2) {
for (int i = 1; i <= len1 - len2; i++) {
pHead1 = pHead1.next;
}
} else if (len2 > len1) {
for (int i = 1; i <= len2 - len1; i++) {
pHead2 = pHead2.next;
}
}
ListNode ans = null;
while (pHead1 != null) {
if (pHead1.val ==pHead2.val) {
ans = pHead1;
break;
}
pHead1 = pHead1.next;
pHead2 = pHead2.next;
}
return ans;
}
}
18.(一)删除链表中结点
**题目描述:**给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点,返回删除后的链表的头节点。
删除节点的操作:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode removeElements(ListNode head, int val){
ListNode dummy=new ListNode(0); //定义一个虚拟的头节点
ListNode cur=dummy; //cur从虚拟头节点开始遍历,保证了真正的头节点head也可以被删除
cur.next=head;
while(cur!=null&&cur.next!=null){
if(cur.next.val==val){
cur.next=cur.next.next;
}else{
cur=cur.next;
}
}return dummy.next;
}
}
(二)删除链表中重复的结点
题目描述:给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
(排序链表说明重复元素是相邻的)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode deleteDuplicates(ListNode head) {
ListNode cur=head; //头节点
while(cur!=null&&cur.next!=null){ //向后遍历,遍历的终止条件:cur在倒数第二个节点即可
(链表中那个节点指向null即为尾节点)
if(cur.val==cur.next.val){ //相邻节点值相同
cur.next=cur.next.next; //则删除下一节点
}else{
cur=cur.next; //否则cur向下移,继续遍历
}
}return head;
}
}
(三)删除链表中重复的结点
题目描述:给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中没有重复出现的数字
(排序链表说明重复元素是相邻的)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode deleteDuplicates(ListNode head) {
ListNode dummy=new ListNode(0); //定义虚拟头节点
dummy.next=head;
ListNode cur=head; //
ListNode pre=dummy; //定义两个指针
while(cur!=null){
while(cur.next!=null&&cur.val==cur.next.val){ //遇到相邻重复节点
cur=cur.next;
}
cur=cur.next;
if(pre.next.next==cur){ //没有遇到重复节点
pre=pre.next; //pre指针向前移动1位
}else{
pre.next=cur; //遇到重复节点,pre指针移动到cur指针位置
}
}return dummy.next;
}
}
25、合并两个排序的链表
**题目描述:**输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
两个链表基于大小比较同时遍历
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode dummp =new ListNode(0); //建立虚拟头节点,
ListNode cur=dummp;
//基于比较,遍历两个链表
while(l1!=null&&l2!=null){
if(l1.val<l2.val){
cur.next=l1; //将cur指针指向更小的节点
cur=cur.next; //继续向下遍历,cur起到串联的作用
l1=l1.next; //l1是比较的基准,移动到下一个基准
}else{
cur.next=l2;
cur=cur.next;
l2=l2.next;
}
}if(l1!=null){
cur.next=l1;
}else{
cur.next=l2;
}
return dummp.next;
}
}
35、复杂链表的复制
**题目描述:**请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
复制节点值:
复制next、random指向关系:
/*
// Definition for a Node.
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
*/
class Solution {
public Node copyRandomList(Node head) {
HashMap<Node,Node> map=new HashMap<>(); //定义HashMap来存储师徒节点
Node cur=head; //定义cur为遍历节点
//第一次遍历,复制节点值,存value值
while(cur!=null){
map.put(cur,new Node(cur.val)); //key为师傅节点,value为徒弟节点
cur=cur.next; //向下遍历
}
//第二次遍历,复制next、random的指向关系
cur=head; //遍历前让cur回到head指针
while(cur!=null){
map.get(cur).next=map.get(cur.next);
map.get(cur).random=map.get(cur.random);
cur=cur.next;
}
return map.get(head); //返回对应的徒弟的头节点
}
}