第一关链表(白银)

2024-03-11 15:30
文章标签 链表 白银 第一关

本文主要是介绍第一关链表(白银),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

第一关链表(白银)

链表高频题目讲解

1.输入两个链表,找出他们第一个公共节点

在这里插入图片描述
当拿到一个题目无从下手的时候,我们可以在自己脑海里面把知道的方法全部过一遍,然后在从前往后思考大概有哪些比较可行呢.
数据结构:数组、链表、队、栈、hash、集合、树、堆、图;
算法思想:查找、排序、双指针、递归、迭代、分治、贪心、回溯和动态规划
万变不离其中,在脑海中过一遍会更容易找到对应的解法
这一道题目的关键是如何找到公共点,这个公共点有这样一个特点,他是node(a1)和node(a2)的共同next,并且在他之后两个链表的节点就都相同了,
知道这个条件之后我们就开始思考有哪些解法
链表: 暴力破解,拿一个链表从头到位的节点从头到位去遍历另外一个链表从头到尾的节点,找到相同的节点就是公共节点,时间复杂度大
队: 不合适
栈: 将链表压栈,拿另外一个链表遍历,找到不同的为止
集合: 使用set集合
hash: 找相同

1.1 hash和集合还有栈

/*** 方法1:通过Hash辅助查找** @param pHead1* @param pHead2* @return*/public static ListNode findFirstCommonNodeByMap(ListNode pHead1, ListNode pHead2) {if (pHead1==null || pHead2==null){return null;}HashMap<ListNode,Integer> hashMap=new HashMap<>();ListNode currentNode01=pHead1;ListNode currentNode02=pHead2;while (currentNode01!=null){hashMap.put(currentNode01,null);currentNode01=currentNode01.next;}while (currentNode02!=null){if (hashMap.containsKey(currentNode02)){return currentNode02;}currentNode02=currentNode02.next;}return null;}/*** 方法2:通过集合来辅助查找** @param headA* @param headB* @return*/public static ListNode findFirstCommonNodeBySet(ListNode headA, ListNode headB) {if (headA==null || headB==null){return null;}Set<ListNode> listNodeSet=new HashSet<>();ListNode currentNode01=headA;ListNode currentNode02=headB;while (currentNode01!=null){listNodeSet.add(currentNode01);currentNode01=currentNode01.next;}while (currentNode02!=null){if (listNodeSet.contains(currentNode02)){return currentNode02;}currentNode02=currentNode02.next;}return null;}/*** 方法3:通过栈*/public static ListNode findFirstCommonNodeByStack(ListNode headA, ListNode headB) {if (headA==null || headB==null){return null;}Stack<ListNode> nodeStack01=new Stack<>();Stack<ListNode> nodeStack02=new Stack<>();ListNode currentNode01=headA;ListNode currentNode02=headB;while (currentNode01!=null){nodeStack01.add(currentNode01);currentNode01=currentNode01.next;}while (currentNode02!=null){nodeStack02.add(currentNode02);currentNode02=currentNode02.next;}ListNode preNode=null;while (nodeStack01.size()>0 && nodeStack02.size()>0){if (nodeStack01.peek()==nodeStack02.peek()){preNode=nodeStack01.pop();nodeStack02.pop();}else {break;}}return preNode;}

1.2 序列拼接和差值实现
序列拼接:两边找公共点难点的就是两个链表的长度不一致 无法进行一一比较 所以 我们就要把两个链表拼接起来 形成两个长度一样的链表 这样子然后再起遍历取公共点就可以了
差值实现:同理这里的差值实现就是把两个长度不等的链表先把长的截取得和短的一样,进行遍历比较即可

/*** 方法4:通过序列拼接*/public static ListNode findFirstCommonNodeByCombine(ListNode pHead1, ListNode pHead2) {if(pHead1==null || pHead2==null){return null;}ListNode p1=pHead1;ListNode p2=pHead2;while(p1!=p2){p1=p1.next;p2=p2.next;//这里的步骤比较精妙,我们始终是在找寻公共点,多余的点通过循环去掉即可if (p1!=p2){if (p1==null){p1=pHead2;}if (p2==null){p2=pHead1;}}}return p1;}/*** 方法5:通过差值来实现** @param pHead1* @param pHead2* @return*/public static ListNode findFirstCommonNodeBySub(ListNode pHead1, ListNode pHead2) {if (pHead1 == null || pHead2 == null) {return null;}ListNode current1 = pHead1;ListNode current2 = pHead2;int l1 = 0, l2 = 0;while (current1 != null) {current1 = current1.next;l1++;}while (current2 != null) {current2 = current2.next;l2++;}current1 = pHead1;current2 = pHead2;int sub = l1 > l2 ? l1 - l2 : l2 - l1;if (l1 > l2) {int a = 0;while (a < sub) {current1 = current1.next;a++;}}if (l1 < l2) {int a = 0;while (a < sub) {current2 = current2.next;a++;}}while (current2 != current1) {current2 = current2.next;current1 = current1.next;}return current1;}

2.判断链表是否为回文序列

找回文序列有很多方法,快慢指针,栈,递归

/*** 方法1:通过双指针的方式来判断** @param head* @return*/public static boolean isPalindromeByTwoPoints(ListNode head) {if (head==null || head.next==null){return true;}ListNode slow=head,fast=head;ListNode pre=head,prepre=null;while(fast!=null && fast.next!=null){pre=slow;slow=slow.next;fast=fast.next.next;pre.next=prepre;prepre=pre;}if (fast!=null){slow=slow.next;}while(pre!=null && slow!=null){if (pre.val!= slow.val){return false;}pre=pre.next;slow=slow.next;}return true;}/*** 方法2:全部压栈** @param head* @return*/public static boolean isPalindromeByAllStack(ListNode head) {if (head==null){return false;}ListNode temp = head;Stack<Integer> stack = new Stack();//把链表节点的值存放到栈中while (temp != null) {stack.push(temp.val);temp = temp.next;}//然后再出栈while (head != null) {if (head.val != stack.pop()) {return false;}head = head.next;}return true;}/*** 方法3:只将一半的数据压栈** @param head* @return*/public static boolean isPalindromeByHalfStack(ListNode head) {if (head == null)return true;ListNode temp = head;Stack<Integer> stack = new Stack();//链表的长度int len = 0;//把链表节点的值存放到栈中while (temp != null) {stack.push(temp.val);temp = temp.next;len++;}//len长度除以2len >>= 1;//然后再出栈while (len-- >= 0) {if (head.val != stack.pop())return false;head = head.next;}return true;}/*** 方法4:通过递归来实现*/static ListNode temp;public static boolean isPalindromeByRe(ListNode head) {temp=head;boolean check = check(head);return check;}private static boolean check(ListNode head) {if (head==null)return true;boolean check=check(head.next) && (head.val==temp.val);temp=temp.next;return check;}

3.合并有序链表

/*** 方法1:面试时就能写出来的方法** @param list1* @param list2* @return*/public static ListNode mergeTwoLists(ListNode list1, ListNode list2) {// write code hereListNode newHead = new ListNode(-1);ListNode res = newHead;while (list1 != null || list2 != null) {if (list1 != null && list2 != null) {//都不为空的情况if (list1.val < list2.val) {newHead.next = list1;list1 = list1.next;} else if (list1.val > list2.val) {newHead.next = list2;list2 = list2.next;} else { //相等的情况,分别接两个链newHead.next = list2;list2 = list2.next;newHead = newHead.next;newHead.next = list1;list1 = list1.next;}newHead = newHead.next;} else if (list1 != null && list2 == null) {newHead.next = list1;list1 = list1.next;newHead = newHead.next;} else if (list1 == null && list2 != null) {newHead.next = list2;list2 = list2.next;newHead = newHead.next;}}return res.next;}/*** 方法2:比方法1更加精简的实现方法** @param l1* @param l2* @return*/public static ListNode mergeTwoListsMoreSimple(ListNode l1, ListNode l2) {ListNode prehead = new ListNode(-1);ListNode prev = prehead;while (l1 != null && l2 != null) {if (l1.val <= l2.val) {prev.next = l1;l1 = l1.next;} else {prev.next = l2;l2 = l2.next;}prev = prev.next;}// 最多只有一个还未被合并完,直接接上去就行了,这是链表合并比数组合并方便的地方prev.next = l1 == null ? l2 : l1;//链表都是对象,存的都是地址,所以本质上还是一样的return prehead.next;}/*** 方法3:通过递归方式来实现** @param l1* @param l2* @return*/public static ListNode mergeTwoListsByRe(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;}}/*** 合并K个链表** @param lists* @return*/public static ListNode mergeKLists(ListNode[] lists) {ListNode res = null;for (ListNode list : lists) {res = mergeTwoListsMoreSimple(res, list);}return res;}

在这里插入图片描述

/*** 给你两个链表 list1 和 list2,它们包含的元素分别为 n 个和 m 个。请你将 list1 中下标从a到b的节点删除,并将list2 接在被删除节点的位置。** @param l1* @param l2* @return*/public static ListNode mergeInBetween(ListNode l1,int a,int b, ListNode l2) {ListNode pre1=l1,post1=l1,post2=l2;int i=0,j=0;while(pre1!=null && post2!=null && j<b){if (i!=a-1){pre1=pre1.next;i++;}if (j!=b){post1=post1.next;j++;}}//因为上面的循环条件导致少了一次,现在补上去post1=post1.next;while(post2.next!=null){post2=post2.next;}pre1.next=l2;post2.next=post1;return l1;}

4.双指针专题

4.1 寻找链表倒数第K个结点

public static ListNode getKthFromEnd(ListNode head, int k) {ListNode slow=head;ListNode fast=head;while(fast != null && k>0){fast=fast.next;k--;}while(fast!=null){slow=slow.next;fast=fast.next;}return slow;}

4.2 寻找链表中间结点

public static ListNode middleNode(ListNode head) {ListNode slow=head;ListNode fast=head;while (fast!=null && fast.next!=null){slow=slow.next;fast=fast.next.next;}return slow;}

4.3 旋转链表

public static ListNode rotateRight(ListNode head, int k) {if (head == null || k == 0) {return head;}ListNode temp = head;ListNode fast = head;ListNode slow = head;int len = 0;while (head != null) {head = head.next;len++;}if (k % len == 0) {return temp;}while ((k % len) > 0) {k--;fast = fast.next;}while (fast.next != null) {fast = fast.next;slow = slow.next;}//链表从始至终都是对象,改变的只是链表的位置,只要没对链表进行操作,链表始终是原来的链表ListNode res = slow.next;slow.next = null;fast.next = temp;return res;}

5.删除链表元素专题

5.1 删除给定的节点

/*** 删除特定值的结点** @param head* @param val* @return*/public static ListNode removeElements(ListNode head, int val) {ListNode dummyHead = new ListNode(0);dummyHead.next = head;ListNode temp = dummyHead;while (temp.next != null) {if (temp.next.val == val) {temp.next = temp.next.next;} else {temp = temp.next;}}return dummyHead.next;}

5.2 找链表的倒数第K个元素开始的链表

/*** 找链表的倒数第K个元素开始的链表** @param head* @param k* @return*/public static ListNode getKthFromEnd(ListNode head, int k) {ListNode pre = head;ListNode curr = head;while (curr != null) {curr = curr.next;if (k > 0)k--;else {pre = pre.next;}}return pre;}/*** 方法1:删除倒数第N个结点** @param head* @param n* @return*/public static ListNode removeNthFromEndByLength(ListNode head, int n) {ListNode dummy = new ListNode(0);dummy.next = head;int length = getLength(head);ListNode cur = dummy;for (int i = 1; i < length - n + 1; ++i) {cur = cur.next;}cur.next = cur.next.next;ListNode ans = dummy.next;return ans;}/*** 方法2:通过栈实现** @param head* @param n* @return*/public static ListNode removeNthFromEndByStack(ListNode head, int n) {ListNode dummy = new ListNode(0);dummy.next = head;Deque<ListNode> stack = new LinkedList<ListNode>();ListNode cur = dummy;while (cur != null) {stack.push(cur);cur = cur.next;}for (int i = 0; i < n; ++i) {stack.pop();}ListNode prev = stack.peek();prev.next = prev.next.next;ListNode ans = dummy.next;return ans;}/*** 方法3:通过双指针** @param head* @param n* @return*/public static ListNode removeNthFromEndByTwoPoints(ListNode head, int n) {ListNode dummy = new ListNode(0);dummy.next = head;ListNode first = head;ListNode second = dummy;for (int i = 0; i < n; ++i) {first = first.next;}while (first != null) {first = first.next;second = second.next;}second.next = second.next.next;ListNode ans = dummy.next;return ans;}

5.3 重复元素保留一个和重复元素都不要

/*** 重复元素保留一个** @param head* @return*/public static ListNode deleteDuplicate(ListNode head) {if (head == null) {return head;}ListNode cur = head;while (cur.next != null) {if (cur.val == cur.next.val) {cur.next = cur.next.next;} else {cur = cur.next;}}return head;}/*** 重复元素都不要** @param head* @return*/public static ListNode deleteDuplicates(ListNode head) {if (head == null) {return head;}ListNode dummy = new ListNode(0);dummy.next = head;ListNode cur = dummy;while (cur.next != null && cur.next.next != null) {if (cur.next.val == cur.next.next.val) {int x = cur.next.val;while (cur.next != null && cur.next.val == x) {cur.next = cur.next.next;}} else {cur = cur.next;}}return dummy.next;}

这篇关于第一关链表(白银)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/798218

相关文章

csu1329(双向链表)

题意:给n个盒子,编号为1到n,四个操作:1、将x盒子移到y的左边;2、将x盒子移到y的右边;3、交换x和y盒子的位置;4、将所有的盒子反过来放。 思路分析:用双向链表解决。每个操作的时间复杂度为O(1),用数组来模拟链表,下面的代码是参考刘老师的标程写的。 代码如下: #include<iostream>#include<algorithm>#include<stdio.h>#

深入手撕链表

链表 分类概念单链表增尾插头插插入 删尾删头删删除 查完整实现带头不带头 双向链表初始化增尾插头插插入 删查完整代码 数组 分类 #mermaid-svg-qKD178fTiiaYeKjl {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-

建立升序链表

题目1181:遍历链表 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:2744 解决:1186 题目描述: 建立一个升序链表并遍历输出。 输入: 输入的每个案例中第一行包括1个整数:n(1<=n<=1000),接下来的一行包括n个整数。 输出: 可能有多组测试数据,对于每组数据, 将n个整数建立升序链表,之后遍历链表并输出。 样例输

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟) 题目描述 给定一个链表,链表中的每个节点代表一个整数。链表中的整数由 0 分隔开,表示不同的区间。链表的开始和结束节点的值都为 0。任务是将每两个相邻的 0 之间的所有节点合并成一个节点,新节点的值为原区间内所有节点值的和。合并后,需要移除所有的 0,并返回修改后的链表头节点。 思路分析 初始化:创建一个虚拟头节点

学习记录:js算法(二十八):删除排序链表中的重复元素、删除排序链表中的重复元素II

文章目录 删除排序链表中的重复元素我的思路解法一:循环解法二:递归 网上思路 删除排序链表中的重复元素 II我的思路网上思路 总结 删除排序链表中的重复元素 给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。 图一 图二 示例 1:(图一)输入:head = [1,1,2]输出:[1,2]示例 2:(图

【数据结构与算法 | 灵神题单 | 删除链表篇】力扣3217, 82, 237

总结,删除链表节点问题使用到列表,哈希表,递归比较容易超时,我觉得使用计数排序比较稳,处理起来也不是很难。 1. 力扣3217:从链表中移除在数组中的节点 1.1 题目: 给你一个整数数组 nums 和一个链表的头节点 head。从链表中移除所有存在于 nums 中的节点后,返回修改后的链表的头节点。 示例 1: 输入: nums = [1,2,3], head = [1,2,3,

c++ 链表详细介绍

链表是数据结构的一种,由节点组成,每个节点包含数据和指向下一个节点的指针。链表在C++中的实现可以是单链表、双链表或循环链表。以下是链表的详细介绍: 1. 单链表 结构: 节点(Node):每个节点包含数据和一个指针(next),指向链表中的下一个节点。 示例结构: struct Node {int data;Node* next;Node(int d) : data(d), next(

带头结点的线性链表的基本操作

持续了好久,终于有了这篇博客,链表的操作需要借助图像模型进行反复学习,这里尽可能的整理并记录下自己的思考,以备后面复习,和大家分享。需要说明的是,我们从实际应用角度出发重新定义了线性表。 一. 定义 从上一篇文章可以看到,由于链表在空间的合理利用上和插入、删除时不需要移动等优点,因此在很多场合下,它是线性表的首选存储结构。然而,它也存在某些实现的缺点,如求线性表的长度时不如顺序存储结构的

数据结构基础(栈,队列,数组,链表,树)

栈:后进先出,先进后出 队列:先进先出,后进后出 数组:查询速度快,通过地址值和索引定位,查询任意数据消耗时长相同,在内存中是连续存储的,删除效率低,要将原始数据删除,然后后面的数据前移,添加效率低,添加索引位置的元素,剩下的都需要向前后移动 链表:节点的存储位置(地址)里面存储本身的数据值,和下一个节点的地址值,链表中的节点是独立对象,在内存中是不连续的。查询速度慢,无论查询哪个数据都要从

(六十四)第 10 章 内部排序(静态链表的插入排序)

示例代码 staticLinkList.h // 静态链表的插入排序实现头文件#ifndef STATIC_LINK_LIST_H#define STATIC_LINK_LIST_H#include "errorRecord.h"#define SIZE 100#define NUM 8typedef int InfoType;typedef int KeyType;ty