关于单向不循环链表的介绍已经在前面的文章给出,本篇文章我们进行习题练习。
1. 删除链表中等于给定值 val 的所有节点。. - 力扣(LeetCode)
思路:遍历链表,当值等于val时将前一节点的next存储为当前节点的next。但因为是单向链表,所以要是想获得前一节点需要一个新的对象在cur遍历时在它前一个节点的位置。
本题比较巧妙的是pre的取值,不用另外假设删除头节点的情况,pre为null时则能代表,很好的解决了整个链表都是要删除的节点的问题。
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode cur = head; //从头节点开始遍历
ListNode pre = null; //cur的前节点
while(cur != null){
if(cur.val == val){ //当cur节点的值与val相等时,分两种情况
if(pre == null){ //pre为空时说明该删除头节点
head = cur.next; //直接将头节点下移即可
}else{ //当其他节点该删除时
pre.next = cur.next;
}
}else{ //cur节点不是要删除的节点
pre = cur; //pre往前走到cur位置
}
cur = cur.next; //cur往前走一步
}
return head;
}
}
2. 反转一个单链表。 . - 力扣(LeetCode)
![](https://img-blog.csdnimg.cn/direct/5bc938a2a9c14987a2f6e930b93d2f03.png)
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode next = null;
ListNode cur = head;
while(cur != null){
next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}
}
3. 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。. - 力扣(LeetCode)
思路:本题主要使用数学思维来解决,一段距离内,a使用b的速度的二倍进行移动,ab一起出发,当a到达终点时b所在的位置就是这段距离的中间位置。
需要注意while的判定条件,已写在对应字段的注释上。
class Solution {
public ListNode middleNode(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while( fast != null && fast.next != null){ //需要先判断当前节点不为空后判断next节点,否则会造成空指针异常。
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
}
4. 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。. - 力扣(LeetCode)
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1 == null && l2 == null) return null;
if(l1 == null || l2 == null) return l1 == null ? l2 : l1;
ListNode head =new ListNode();
ListNode node = head;
while(l1 != null && l2 != null){
if(l1.val >= l2.val){
node.next = l2;
l2 = l2.next;
}else{
node.next = l1;
l1 = l1.next;
}
node = node.next;
}
if(l1 != null){
node.next = l1;
}
if(l2 != null){
node.next = l2;
}
return head.next;
}
}
5.以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前 。链表分割_牛客题霸_牛客网
思路:先创建两个空链表用来存储大于x和小于x的元素,遍历pHead链表,比x大的放在right链表,小于的放在left,最后将left与right串联在一起即可。
需要注意,创建两个链表的头节点都不是我们想要的节点,所以在提交节点时或者在串联两个链表时都应该获取当前节点的next节点。串联后记得将后一节点置为空,否则会有两个节点同时指向前链表的末尾,造成错误和空间的浪费。
public class Partition {
public ListNode partition(ListNode pHead, int x) {
ListNode right = new ListNode(x);
ListNode node = right;
ListNode left = new ListNode(0);
ListNode cur = left;
while(pHead != null){
if(pHead.val < x ){
cur.next = pHead;
cur = cur.next;
}else{
node.next = pHead;
node = node.next;
}
pHead = pHead.next;
}
cur.next = right.next;
node.next = null;
return left.next;
}
}
6.链表的回文结构。链表的回文结构_牛客题霸_牛客网
回文结构是指123321类似这样的结构,我们写过单链表的倒置,所以本题思路我们需要找到中间节点然后倒置节点后面的元素,在对这两个链表进行遍历比对看一不一样即可。
需要注意,本题应该分为奇数个节点和偶数个节点的情况区别在于123321与12321
public class PalindromeList {
public boolean chkPalindrome(ListNode A) {
ListNode fast = A;
ListNode slow = A;
while(fast != null && fast.next != null){ //偶数情况
fast = fast.next.next;
slow = slow.next;
}
ListNode slowList = slow; //以中间节点为头节点的后半段链表
if(fast != null){ //奇数
slow = slow.next;
while(slow != null){ //倒置操作
ListNode cur = slow.next;
slow.next = slowList;
slowList = slow;
slow = cur;
}
}else{
while(slow != null){
ListNode cur = slow.next;
slow.next = slowList;
slowList = slow;
slow = cur;
}
}
while(A != slowList ){ //将两个链表进行比对
if(A.val == slowList.val){
A=A.next;
slowList = slowList.next;
}else{
return false;
}
}
return true;
}
}
7.输入两个链表,找出它们的第一个公共结点。. - 力扣(LeetCode)
思路:a和b遍历两个链表,当遍历完一个链表后遍历另一个链表,当ab路程相同时的节点便是相交节点。
需要注意本题使用三目运算符是最优解,当当前节点为空时跳转另一个链表,不为空时移动到下一节点,跳出循环时就是相交节点。
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA == null || headB == null){
return null;
}
ListNode pA = headA;
ListNode pB = headB;
while(pA != pB){
pA = pA == null? pB : pA.next; //三目运算符
pB = pB == null ? pA : pB.next;
}
return pA; //循环结束则pApB相交
}
}
8.给定一个链表,判断链表中是否有环。. - 力扣(LeetCode)
思路:本题思路主要是数学思维,如果有环的情况下,设置一个指针一次走两步,一个指针一次走一步,在走到null前两个指针必定相遇。如果跳出循环了则说明没有环。
public class Solution {
public boolean hasCycle(ListNode head) {
if(head == null || head.next == null ){
return false;
}
ListNode fast = head;
ListNode slow = head;
while(fast != null&& fast.next != null ){
fast = fast.next.next;
slow = slow.next;
if(fast == slow){
return true;
}
}
return false;
}
}
9.给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 NULL。. - 力扣(LeetCode)
该题算是上一题的进阶版,思路就是在上一题的基础上,先判断有没有环,判断有环后一个指针在相遇位置开始移动,一个指针在初始位置移动,当两个指针相遇的节点就是入环节点。
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
boolean intersect = false; //用来判断是否有环的变量
while(fast != null && fast.next != null ){
fast = fast.next.next;
slow = slow.next;
if(fast == slow){
intersect = true;
break;
}
}
if(!intersect){ //如果没环则返回空
return null;
}
slow = head;
while(slow != fast){
slow = slow.next;
fast = fast.next;
}
return slow;
}
}