代码随想录(2)-链表
题单
203.移除链表元素
707.设计链表(middle)
206.反转链表
24.两两交换链表中的节点(middle)
19.删除链表的倒数第 N 个结点(middle)
面试题 02.07. 链表相交
142.环形链表II(middle)
链表节点代码
public class ListNode {
// 结点的值
int val;
// 下一个结点
ListNode next;
// 节点的构造函数(无参)
public ListNode() {
}
// 节点的构造函数(有一个参数)
public ListNode(int val) {
this.val = val;
}
// 节点的构造函数(有两个参数)
public ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
203.移除链表元素
题意:删除链表中等于给定值 val 的所有节点。
示例 1:
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
示例 2:
输入:head = [], val = 1
输出:[]
示例 3:
输入:head = [7,7,7,7], val = 7
输出:[]
递归方法(我的思路)
public ListNode removeElements(ListNode head, int val) {
// 如果当前节点为null,直接返回
if (head == null) {
return head;
}
// 遍历更新子节点
head.next = removeElements(head.next, val);
// 如果当前元素为该元素,则返回子节点;否则,返回
if (head.val == val) {
return head.next;
}else{
return head;
}
}
707.设计链表(middle)
在链表类中实现这些功能:
get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
思路:
这题没啥好讲的,就是按照要求认真实现。
class MyLinkedList {
ListNode head;
public MyLinkedList() {
}
public int get(int index) {
ListNode temp = head;
for( int i = 0 ; temp != null; temp = temp.next,i++){
if(i == index){
return temp.val;
}
}
return -1;
}
public void addAtHead(int val) {
ListNode temp = head;
head = new ListNode(val);
head.next = temp;
}
public void addAtTail(int val) {
if( head == null ){
head = new ListNode(val);
return;
}
ListNode temp = head;
while( temp.next != null ){
temp = temp.next;
}
temp.next = new ListNode(val);
}
public void addAtIndex(int index, int val) {
if( index <= 0){
addAtHead(val);
return;
}
ListNode temp = head;
ListNode pre = null;
for( int i = 0 ; temp != null; pre = temp, temp = temp.next, i++){
if(i == index){
pre.next = new ListNode(val, temp);
return;
}
}
if(pre != null && pre.next == null){
pre.next = new ListNode(val);
}
}
public void deleteAtIndex(int index) {
ListNode temp = head;
ListNode pre = null;
for( int i = 0 ; temp != null; pre = temp, temp = temp.next,i++){
if(i == index){
if(pre == null){
head = temp.next;
}else{
pre.next = temp.next;
}
return;
}
}
}
}
206.反转链表
题意:反转一个单链表。
示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
思路:遍历所有元素,修改节点指针为前一个节点
class Solution {
public ListNode reverseList(ListNode head) {
ListNode reverseHead = null;
while( head != null ){
ListNode next = head.next;
head.next = reverseHead;
reverseHead = head;
head = next;
}
return reverseHead;
}
}
24.两两交换链表中的节点(middle)
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
思路:
当前元素且子元素不为空。
--保存子元素的next为temp,以供当前元素更新
--设置子元素的next为当前元素
--设置前一个节点的next为子元素(如果前一个为空,则设子元素为head)
--更新前一个为当前节点
--设置当前节点的next为temp
--更新当前节点为temp
public ListNode swapPairs(ListNode head) {
ListNode pre = null;
ListNode node = head;
while(node!= null && node.next != null){
// --保存子元素的next为temp,以供当前元素更新
ListNode temp = node.next.next;
// --设置子元素的next为当前元素
node.next.next = node;
// --设置前一个节点的next为子元素(如果前一个为空,则设子元素为head)
if( pre == null){
head = node.next;
}else{
pre.next = node.next;
}
// --更新前一个为当前节点
pre = node;
// --设置当前节点的next为temp
node.next = temp;
// --更新当前节点为temp
node = temp;
}
return head;
}
参考代码后,精简了循环体中的判断语句。
public ListNode swapPairs(ListNode head) {
ListNode pre = new ListNode();
ListNode preHead = pre;
pre.next = head;
while(head!= null && head.next != null){
// --保存子元素的next为temp,以供当前元素更新
ListNode temp = head.next.next;
// --设置子元素的next为当前元素
head.next.next = head;
// --设置前一个节点的next为子元素
pre.next = head.next;
// --更新前一个为当前节点
pre = head;
// --设置当前节点的next为temp
head.next = temp;
// --更新当前节点为temp
head = temp;
}
return preHead.next;
}
19.删除链表的倒数第 N 个结点(middle)
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
进阶:你能尝试使用一趟扫描实现吗?
思路:
暴力做法,可以扫描两遍,第一遍获得链表长度,然后判断处理倒数第n个位置。
题目提示使用一次扫描,那么可以尝试保存遍历元素前第n个元素,一次遍历即可获取最后一个元素的前n个元素信息。
设置虚拟头节点preHead=pre
遍历链表,当前元素不为空
--若遍历元素个数大于n,pre更新为下一个
删除pre下一个
返回虚拟头节点的下一个
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode pre = new ListNode(-1,head);
ListNode preHead = pre;
int counter = 0;
while(head != null){
if( ++counter > n){
pre = pre.next;
}
head = head.next;
}
pre.next = pre.next.next;
return preHead.next;
}
面试题 02.07. 链表相交
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
注意,函数返回结果后,链表必须 保持其原始结构 。
思路
获取两个字符长度
判断长串a和短串b
将a指针移动到倒数b长度位置
同时遍历a、b
-- 若二者相等则直接返回该节点
返回null
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
int a = 0,b = 0;
for(ListNode temp = headA; temp != null; temp = temp.next ) a++;
for(ListNode temp = headB; temp != null; temp = temp.next ) b++;
ListNode curLong = headA;
ListNode curShort = headB;
int gap = a - b;
if( a < b ){
curLong = headB;
curShort = headA;
gap = -gap;
}
while( gap-- > 0 ){
curLong = curLong.next;
}
while( curLong != null ){
if( curLong == curShort ){
return curLong;
}
curLong = curLong.next;
curShort = curShort.next;
}
return null;
}
142.环形链表II(middle)
题意: 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
思路:
其实没什么思路,看了题解的提示,采用快慢指针法。
快指针fast走两步,慢指针走一步
遍历快指针
-- 如果等于快指针,返回
返回null
上面遗漏考虑了,只返回了相遇节点,没有考虑入环节点。
如何查找入环点,参考了题解。
快慢指针法
public ListNode detectCycle(ListNode head) {
// >快指针fast走两步,慢指针走一步
ListNode fast = head, slow = head;
// 遍历快指针
while( fast != null ){
slow = slow.next;
if(fast.next == null) return null;
else fast = fast.next.next;
if(fast == slow ){
for(; slow != head; slow = slow.next, head = head.next );
return slow;
}
}
return null;
}
总结
链表的虚拟头节点可以很好的避免头节点的前一个节点的判断。
快慢指针法 很巧妙。