目录
3. 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。
5. 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
6. 编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前 。
前言:
在些数据结构算法题,画图分析无疑是一把利刃。
在脑海里数据结构中单链表的结构可以理解为下图
1. 删除链表中等于给定值 val 的所有节点。
题目链接:
题目描述:
给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
测试样例:
代码实现:
思路:
class Solution {
public ListNode removeElements(ListNode head, int val) {
// 头结点为空的情况
if(head == null) {
return null;
}
// 定义一个结点遍历整条链表,头结点最后考虑
// 定义一个前驱,方便删除操作
ListNode cur = head.next;
ListNode prev = head;
// 遍历到cur为空,整条链表可以遍历完
while(cur != null ) {
if(cur.val==val) {
prev.next = cur.next;
cur = cur.next;
}else{
prev = cur;
cur = cur.next;
}
}
// 头结点和头结点后一个 都要删除的情况 到这一步都在这不存在(所以判断头结点放到最后),
// 画图便知
if(head.val==val) {
head = head.next;
}
return head;
}
}
2. 反转一个单链表。
题目链接:
题目描述:
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
测试样例:
代码实现:
思路:从第二个节点开始,头插法实现逆转
class Solution {
public ListNode reverseList(ListNode head) {
if(head==null) {
return null;
}
ListNode cur = head.next;
head.next = null;
while(cur!=null) {
ListNode curNext = cur.next;
cur.next = head;
head = cur;
cur = curNext;
}
return head;
}
}
3. 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。
题目链接:
题目描述:
给你单链表的头结点 head
,请你找出并返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
测试样例:
思路:同样的路程下,fast的速度是slow的两倍,当fast到达终点时,slow到达中间
代码实现:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode middleNode(ListNode head) {
if(head==null) {
return null;
}
ListNode slow = head;
ListNode fast = head;
while(fast!=null&&fast.next!=null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
}
4. 输入一个链表,输出该链表中倒数第k个结点。
题目链接:
题目描述:
输入一个链表,输出该链表中倒数第k个结点。
测试样例:
思路:1.让fast先走k-1步
2.fast和slow同时走,因为fast先走了k-1步,所以fast和slow永远相差k步
3.当fast到终点时,slow和fast相差k步,所以slow就为倒数第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) {
//如果k是负数或者很大,为非法情况
// if(k<=0||k>size())
// 但是这里size() 已经遍历了一次
// 所以在for循环后面 判断 如果fast为空了 直接return
if(k <=0 || head == null ) {
return null;
}
ListNode fast = head;
ListNode slow = head;
for(int i = 0;i < k-1; i++) {
fast = fast.next;
if(fast == null) {
return null;
}
}
while(fast.next!= null ){
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
5. 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
题目链接:
题目描述:
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
思路:
代码实现:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode newHead = new ListNode();
ListNode tHead = newHead;
ListNode head1 = list1;
ListNode head2 = list2;
while(head1!=null&&head2!=null) {
if(head1.val<head2.val) {
tHead.next = head1;
head1=head1.next;
}else{
tHead.next = head2;
head2 = head2.next;
}
tHead = tHead.next;
}
if(head1!=null) {
tHead.next = head1;
}
if(head2!=null) {
tHead.next = head2;
}
return newHead.next;
}
}
6. 编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前 。
题目链接:
题目描述:
现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。
思路:这里x取20
通过遍历整条链表,将数据分为两组,一直是在20之前的,一组在之后的
特殊情况:
代码实现:
import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Partition {
public ListNode partition(ListNode pHead, int x) {
// write code here
ListNode bs = null;
ListNode be = null;
ListNode as = null;
ListNode ae = null;
ListNode cur = pHead;
while(cur!=null) {
if(cur.val<x) {
//第一次插入
if(bs==null) {
bs = be = cur;
}else{
be.next = cur;
be = be.next;
}
}else{
//第一次插入
if(as==null) {
as = ae =cur;
}else{
ae.next = cur;
ae = ae.next;
}
}
cur = cur.next;
}
//前面一段没有数据的情况
if(bs==null) {
return as;
}
be.next = as;
// 原链表最后一个数字不是最大的情况,容易造成死循环
if(ae!=null) {
ae.next = null;
}
return bs;
}
}
7. 链表的回文结构。
题目链接:
题目描述:
对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。
给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。
测试样例:
思路:
为上面第三题和第五题的组合
1.找到链表的中间节点
2.翻转中间节点以后的链表
3.从前 从后 同时往前遍历 判断是否回文
代码实现:
import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class PalindromeList {
public boolean chkPalindrome(ListNode A) {
// write code here
ListNode slow = A;
ListNode fast = A;
//找到中间节点
while(fast!=null&&fast.next!=null) {
slow = slow.next;
fast = fast.next.next;
}
//逆转后半段节点
ListNode cur = slow.next;
while(cur!=null) {
ListNode curNext = cur.next;
cur.next = slow;
slow = cur;
cur = curNext;
}
//同时向中间靠拢
ListNode Ahead = A;
while(A!=slow) {
if(A.val!=slow.val) {
return false;
}
if(A.next == slow) {
return true;
}
A = A.next;
slow = slow.next;
}
return true;
}
}
8. 输入两个链表,找出它们的第一个公共结点。
题目链接:
题目描述:
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null
。
测试样例:
思路:
1.相交只能是Y字形
2.两个链表长度不一样主要体现在它们相交前
3.可以让最长的链表引用先走他们的差值步
代码实现:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
int lenA = 0;
int lenB = 0;
int len = 0;
// 假定最长的是A
ListNode pL = headA;
ListNode pS = headB;
//长度
while(pL!=null) {
lenA++;
pL = pL.next;
}
while(pS!=null) {
lenB++;
pS = pS.next;
}
len = lenA-lenB;
pL = headA;
pS = headB;
//修正指向 和 len的差值
if(len<0) {
pL = headB;
pS = headA;
len = lenB - lenA;
}
//保证 pl指向最长的 ps指向最短的 len 是一个正数
if(pL==null||pS==null) {
return null;
}
//2.让最长的列表走差值步
for(int i = 0;i < len ;i++) {
pL = pL.next;
}
//3.相遇的点
while(pL!=pS) {
pL = pL.next;
pS = pS.next;
}
return pL;
}
}