武功山好漂亮呀~
题目链接: 点击跳转至本题
题目大意:
给定一个非空单链表,要求返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。
解题思路:二倍追击
本题仍是二倍追击问题,这里要求的中间点是右边的,之前在234. 回文链表中写过求中间点左边的。现在来比较一下两者的差异:
while(fast.next != null && fast.next.next != null)
while(fast != null && fast.next != null)
//相当于比上面的循环多走了一步差异主要在while循环中的循环条件。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode middleNode(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
}
题目链接: 点击跳转至本题
题目大意:
给定一个单链表的引用结点 head。此链表是一个整数数字的二进制表示形式。要求返回该链表所表示数字的 十进制值 。
解题思路:反转链表+模拟
二进制转十进制的转换原理:从二进制的右边第一个数开始,每一个数乘以2的n次方,n从0开始,每次递增1。然后得出来的每个数相加即是十进制数。
根据这个特性,可以先将链表反转,然后从低位到高位方便操作。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
/** 二进制转十进制的转换原理:从二进制的右边第一个数开始,每一个数乘以2的n次方,n从0开始,每次递增1。然后得出来的每个数相加即是十进制数。 **/
public int getDecimalValue(ListNode head) {
head = reverseList(head);
int i = 0,result = 0;
while(head != null){
//<<左移运算符:n<< m 相当于n乘以2的m次方
result += head.val * (1 << i++);
head = head.next;
}
return result;
}
//反转链表
private ListNode reverseList(ListNode head){
ListNode pre = null;
ListNode cur = head;
while(cur != null){
ListNode tmp = cur.next;
cur.next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
}
题目链接: 点击跳转至本题
题目大意:输入一个链表的头节点,从尾到头反过来返回每个节点的值,要求用数组返回。
解题思路:数组赋值
先求出链表的长度length,然后直接定义一个长度为length的数组,倒序向数组中装载链表结点的val值。注意是从length-1到0。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public int[] reversePrint(ListNode head) {
//求链表的长度lenth
int length = 0;
ListNode temp = head;
while(temp != null){
length++;
temp = temp.next;
}
//倒序装入数组中
int[] ans = new int[length];
for(int i = length-1;i>=0;i--){
ans[i] = head.val;
head = head.next;
}
return ans;
}
}
题目链接: 点击跳转至本题
题目大意:
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
要求返回删除后的链表的头节点。
解题思路:
此题和上周的203.移除链表元素相同,先设置哨兵结点preHead方便结果的存储。定义pre1和pre2工作指针分别指向preHead和head。遍历过程中,使用将pre2与val值比较,如果相等则将pre1的下一个结点连接上pre2的下一个结点。否则pre1移动到ore2。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode deleteNode(ListNode head, int val) {
//设置哨兵节点
ListNode preHead = new ListNode(0);
preHead.next = head;
//pre1和pre2为工作指针
ListNode pre1 = preHead;
ListNode pre2 = head;
while(pre2 != null){
if(pre2.val == val){
pre1.next = pre2.next;
}else{
pre1 = pre2;
}
pre2 = pre2.next;
}
return preHead.next;
}
}
题目链接: 点击跳转至本题
题目大意:给定一个链表,输出该链表中倒数第k个结点。本题从1开始计数,即链表的尾结点是第1个结点。
解题思路:倒数第k个结点 == 下标为length-k的结点
本题与19. 删除链表的倒数第N个节点,有些类似,但是更简单,只需要记住倒数第k个结点 == 下标为length-k的结点即可。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
//删除倒数第k个结点 == 下标为第length-k的结点
//求链表长度length
int length = 0;
ListNode temp = head;
while(temp != null){
length++;
temp = temp.next;
}
for(int i=0;i<length-k;i++){
head = head.next;
}
return head;
}
}
题目链接: 点击跳转至本题
题目大意:定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
解题思路:反转链表
本题与206. 反转链表相同,经典题目了,整体思路是定义三个指针,pre指针始终指向新链表的表头,cur指针做为遍历的工作指针,temp指针始终指向cur结点的下一个结点。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
//定义pre结点始终指向新链表的头部
ListNode pre = null;
ListNode cur = head;
while(cur != null){
//temp结点始终存储cur结点的下一个结点
ListNode temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
}
题目链接: 点击跳转至本题
解题思路:浪漫指针
本题与160. 相交链表相同,思路是定义两个指针,进行追击,追击方式是每当一个链表走到尽头时,就从对方链表的头节点开始走,直到相遇。
/**
* 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) {
//特判,如果是空链表就直接返回
if(headA==null || headB==null){
return null;
}
//走到尽头见不到你,就走过你来时的路,知道相遇才发现,你也走过我走过的路。
ListNode curA = headA,curB = headB;
while(curA != curB){
curA = curA == null ? headB : curA.next;
curB = curB == null ? headA : curB.next;
}
return curA;
}
}
题目链接: 点击跳转至本题
题目大意:给定一未排序的链表,要求移除链表中的重复节点。
解题思路:set去重
从链表头开始遍历,依次放入set集合中,如果下一个结点的val值已经存在在set集合中,就将其移除掉。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode removeDuplicateNodes(ListNode head) {
Set<Integer> set = new HashSet<>();
ListNode cur = head;
while(cur!=null && cur.next!=null){
set.add(cur.val);
if(set.contains(cur.next.val)){
cur.next = cur.next.next;
}else{
cur = cur.next;
}
}
return head;
}
}
题目链接: 点击跳转至本题
题目大意:给定一个链表,要求返回倒数第k个结点
解题思路:倒数第k个结点 == 下标为length-k的结点
与剑指 Offer 22. 链表中倒数第k个节点类似,只是返回的结果不同。牢记回倒数第k个结点 == 返回下标为第length-k的结点即可。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public int kthToLast(ListNode head, int k) {
//返回倒数第k个结点 == 返回下标为第length-k的结点
int length = 0;
ListNode temp = head;
while(temp != null){
length++;
temp = temp.next;
}
for(int i=0;i<length-k;i++){
head = head.next;
}
return head.val;
}
}
题目链接: 点击跳转至本题
题目大意:要求编写一个函数,可以删除某个链表中给定的(非末尾)节点。传入函数的参数为需要被删除的节点
解题思路:与下一个节点交换
本题与237. 删除链表中的节点相同。给定了需要被删除的结点,而不是前一个结点。此时需要将下一个结点的值赋值给当前节点,并将本节点的下一个节点 指向 下一个节点的指向。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public void deleteNode(ListNode node) {
//将后一个节点的val赋给node
node.val = node.next.val;
//删除node后面的节点
node.next = node.next.next;
}
}
题目链接: 点击跳转至本题
题目大意:给定一个无序链表和一个定值x,返回一个新的链表,要求使得值小于x的元素位于大于等于x的元素前。
解题思路:哨兵节点
此题与86. 分隔链表相同。设置两个哨兵节点L和R分别表示记录值小于x和值大于等于x的链表,遍历时,将小于特定值x的节点链接到L链表上,将大于等于特定值x的节点链接到R链表上,最后将第二个链表链接到第一个链表上。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode partition(ListNode head, int x) {
//给定一个无序链表和一个定值x,返回一个新的链表,要求使得值小于x的元素位于大于等于x的元素前
//定义链表L记录小于x的值,R记录大于等于x的值
ListNode L = new ListNode(0);
ListNode R = new ListNode(0);
ListNode curL = L,curR = R;
while(head != null){
if(head.val < x){
curL.next = new ListNode(head.val);
curL = curL.next;
}else{
curR.next = new ListNode(head.val);
curR = curR.next;
}
head = head.next;
}
//拼接两段链表
curL.next = R.next;
return L.next;
}
}
题目链接: 点击跳转至本题
题目大意:编写一个函数,检查输入的链表是否是回文的。
解题思路:反转链表+二倍追击
本题与 234. 回文链表相同。先将链表从中间分隔开,对后半段进行反转,然后将反转后的链表与前半段进行比较。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isPalindrome(ListNode head) {
ListNode temp = salfList(head);
ListNode L= reversalList(temp);
boolean flag = true;
ListNode p1 = head,p2 = L;
while(flag && p2!=null){
if(p1.val != p2.val){
flag = false;
}
p1 = p1.next;
p2 = p2.next;
}
return flag;
}
//反转链表
private ListNode reversalList(ListNode head){
ListNode pre = null;
ListNode cur = head;
while(cur != null){
ListNode temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
//分割链表
private ListNode salfList(ListNode head){
ListNode slow = head,fast = head;
while(fast != null && fast.next!=null){
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
}
题目链接: 点击跳转至本题
题目大意:给定两个链表,要求返回两个链表相交的起始节点,若无相交节点则返回null。
解题思路:浪漫指针
本题与160. 相交链表和剑指 Offer 52. 两个链表的第一个公共节点相同,思路仍是定义两个指针,进行追击,追击方式是每当一个链表走到尽头时,就从对方链表的头节点开始走,直到相遇。
/**
* 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) {
if(headA == null || headB == null){
return null;
}
ListNode pA = headA,pB =headB;
while(pA != pB){
pA = pA != null ? pA.next:headB;
pB = pB != null ? pB.next:headA;
}
return pA;
}
}
题目链接: 点击跳转至本题
题目大意:
给定两个正序非空链表,求出它们的和。假设这两个链表都不会以0开头。
解题思路:链表反转+模拟加法
可以对两链表进行反转,然后变成类似2. 两数相加的题目,反转链表可以参照206. 反转链表。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode L1 = reverseList(l1);
ListNode L2 = reverseList(l2);
ListNode L3 = new ListNode(1);
//创建指针p,q,v分别指向链表L1,L2,L3
ListNode p = L1, q = L2 ,v = L3;
int carry = 0;
while(p!=null || q!=null){
int x = (p!=null) ? p.val:0;
int y = (q!=null) ? q.val:0;
int sum = x + y + carry;
carry = sum/10;
v.next = new ListNode(sum%10);
v = v.next;
if(p != null) p = p.next;
if(q != null) q = q.next;
}
if(carry > 0){
v.next = new ListNode(1);
}
//注意返回的结果是从L3的下一个节点开始的反转
return reverseList(L3.next);
}
//反转链表
private ListNode reverseList(ListNode head){
ListNode pre = null;
ListNode cur = head;
while(cur != null){
ListNode tmp = cur.next;
cur.next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
}
题目链接: 点击跳转至本题
题目大意:根据给出的方法名和参数,使用单链表或双链表完成链表的设计。
解题思路:单链表的基本增删查
这里使用单链表完成设计,我们的数据结构中需要多定义一个size表示链表的长度,注意添加时对其+1,删除时对其-1。
//定义节点
class ListNode{
int val;
ListNode next;
ListNode(int x){
val = x;
}
}
class MyLinkedList {
int size = 0;
ListNode preHead = null;
/** 在这儿初始化我们的数据结构. */
public MyLinkedList() {
preHead = new ListNode(0);
}
/** 根据index下标获得节点的val **/
public int get(int index) {
if(index < 0 || index >= size){
return -1;
}
ListNode curr = preHead;
for(int i = 0;i <= index;i++){
curr = curr.next;
}
return curr.val;
}
/** 头添加 */
public void addAtHead(int val) {
addAtIndex(0,val);
}
/** 尾添加. */
public void addAtTail(int val) {
addAtIndex(size,val);
}
/** 根据index索引,添加val值. */
public void addAtIndex(int index, int val) {
if(index > size){
return;
}
if(index < 0){
index = 0;
}
size++;
//将pre指向待添加节点的前一个节点
ListNode pre = preHead;
for(int i = 0;i < index;i++){
pre =pre.next;
}
//进行添加操作
ListNode toAdd = new ListNode(val);
toAdd.next = pre.next;
pre.next = toAdd;
}
/** 根据index索引删除节点. */
public void deleteAtIndex(int index) {
if(index < 0 || index >= size){
return;
}
size--;
ListNode pre = preHead;
for(int i = 0;i < index;i++){
pre = pre.next;
}
pre.next = pre.next.next;
}
}
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList obj = new MyLinkedList();
* int param_1 = obj.get(index);
* obj.addAtHead(val);
* obj.addAtTail(val);
* obj.addAtIndex(index,val);
* obj.deleteAtIndex(index);
*/
题目链接: 点击跳转至本题
题目大意:给定两个非空的链表用来表示两个非负的整数,各自的位数按照逆序存储在链表中(每个节点只能存储一位数字)。要求将这两个整数加起来,返回一个新的链表来表示它们的和。(说明:除了数字0之外,这两个整数都不会以0开头。)
解题思路:模拟
本题与2. 两数相加相同。额外创建l3链表用来存储结果,定义cur1,cur2,cur3作为三条链表的工作指针,定义carry作为进位变量(加法只会进1位或不进位),两链表同时从头遍历时,对应位相加的值对10取余作为carry进位变量,对10取模作为存储到l3的数据。待遍历结束,对carry 进行判断,若仍有进位则对cur3的下一个节点补1。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode l3 = new ListNode(0);
//创建指针cur1,cur2,cur3分别指向三个链表
ListNode cur1 = l1,cur2 = l2,cur3 = l3;
//初始化进位遍历carry
int carry = 0;
while(cur1!=null || cur2!=null){
int x = (cur1 != null) ? cur1.val:0;
int y = (cur2 != null) ? cur2.val:0;
int sum = x + y + carry;
carry = sum/10;
cur3.next = new ListNode(sum%10);
cur3 = cur3.next;
if(cur1 != null){
cur1 = cur1.next;
}
if(cur2 != null){
cur2 = cur2.next;
}
}
//遍历结束,进位仍然>0则追加一个值为1的新节点
if(carry > 0){
cur3.next = new ListNode(1);
}
return l3.next;
}
}
题目链接: 点击跳转至本题
题目大意:给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
解题思路:HashSet去重
本题与142. 环形链表 II相同。可以使用不可重复的集合HashSet来进行比较判断,当下一个将要添加到set的节点已经存在再set中的时候,返回下一个节点。
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
Set<ListNode> set = new HashSet<ListNode>();
ListNode node = head;
while(node != null){
if(set.contains(node)){
return node;
}
set.add(node);
node = node.next;
}
return null;
}
}