lc206:反转链表(简单)
题目描述:
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
/**
* 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 reverseList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode pre = null;
ListNode cur = head;
while (cur.next != null) {
ListNode tmp = cur.next;
cur.next = pre;
pre = cur;
cur = tmp;
}
cur.next = pre;
head = cur;
return head;
}
}
解题心得:
这是一道简单题,对于该题确实没有什么可说的,但是反转链表的思想是比较常见的,要学会应用于其它地方。
该方法使用的是双指针,首先我们需要定义一个指针cur指向head头指针(我们要习惯不要使用head指针来操作链表),定义一个pre指针为null,这个是比较重要的,尽量在可以的情况下保持头指针的操作和后面指针操作相同。然后就是暂存指针tmp,防止指针丢失的作用。
lc19:删除链表的倒数第 N 个结点(中等)
题目描述:
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
/**
* 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 removeNthFromEnd(ListNode head, int n) {
ListNode fastIndex = head;
ListNode slowIndex = head;
while (n-- > 0 && fastIndex != null) {
fastIndex = fastIndex.next;
}
if (fastIndex == null) {
if (head.next == null) {
head = null;
return head;
}
head = head.next;
return head;
}
fastIndex = fastIndex.next;
while (fastIndex != null) {
fastIndex = fastIndex.next;
slowIndex = slowIndex.next;
}
slowIndex.next = slowIndex.next.next;
return head;
}
}
解题心得:
说实在的,这道题我还是写了蛮久,就是因为每次需要调试特殊情况。即使是现在的代码,感觉也还是很模糊,并没有做到清晰明了。
说说题,首先我们一看是删除倒数第n个节点,可以想象成一个滑动窗口的首尾,那么这就是一个比较明显的快慢指针问题。
我们的想法就是快指针移到最尾巴,慢指针刚好要比快指针慢n+1个位置(之所以是n+1而不是n,是因为我们将慢指针后面的那个空间定义为待删除数据)
那么我们需要来讨论一些特殊情况:
1)题目没有告诉如果失败会怎样,那么我们默认n一个符合要求的,不存在n<0或者大于链表长度的情况。
2)我们要考虑当快指针比慢指针快n-1时,也就是n正好等于链表长度,那么我们要删除的就是head的那个节点,我们在正常删除节点的时候,此时又有个特殊情况,如果这个链表就只有一个head节点,也就是n和链表长度都等于1的情况,我们删除的方法又是和常规操作不一样的。
3)当n小于了链表长度,我们就可以正常遍历进行删除了,这个疑点并不大,只是在两个指针同时移动之前,我们为什么需要先把快指针移动一下?这个也是比较困惑的地方,因为我们快慢指针停止的终点是fastIndex == null,所以导致我们不能在理想状态下停到最尾巴上,所以我们需要将快慢指针的差距再拉开一个节点。(如果我们能够使得fastIndex停在最后一个位置,是不是就可以不用先移动?是的。------ 我们只需要将后面停止条件设置成fastIndex.next != null就行了)
/**
* 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 swapPairs(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode cur = head.next;
head.next = swapPairs(cur.next);
cur.next = head;
return cur;
}
}
该代码参考了别人的代码。
首先这道题可以使用递归我就没想到,这个还是我太菜了。
递归三部曲:
1)找终止条件:本题终止条件很明显,当递归到链表为空或者链表只剩一个元素的时候,没得交换了,自然就终止了。
2)找返回值:返回给上一层递归的值应该是已经交换完成后的子链表。(交换后的自链表头指针)
3)单次的过程:因为递归是重复做一样的事情,所以从宏观上考虑,只用考虑某一步是怎么完成的。我们假设待交换的俩节点分别为head和cur,cur的应该接受上一级返回的子链表(参考第2步)。接收了之后,就相当于是一个含三个节点的链表交换前两个节点,就很简单了,想不明白的画画图就ok。
递归精髓:每一级的操作是一样的
递归方面的推荐看一下这个博客:递归三部曲
递归套路解题:
lc104:二叉树的最大深度
题目描述:
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
class Solution {
public int maxDepth(TreeNode root) {
if (root == null) {
return 0;
}
int left = maxDepth(root.left);
int right = maxDepth(root.right);
return Math.max(left, right) + 1;
}
}
说实话,这道题自己第一遍还是需要看题解,刷leetcode就是这样,需要重复多次才能有收获。
终止条件:root == null, 这里的root是指每个子节点的根节点。
返回值:首先看返回值是否可以和该算法最终返回值一致,若一致则非常容易找到。本题返回值为子树的最大深度。
单次返回过程:就是分别求左子树和右子树的最大深度,然后比较左右子树深度,取大者。
题目描述:
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
class Solution {
public boolean isBalanced(TreeNode root) {
int res = isBFS(root);
if (res == -1) {
return false;
}
return true;
}
public int isBFS (TreeNode root) {
if (root == null) {
return 0;
}
int left = isBFS(root.left);
int right = isBFS(root.right);
if (Math.abs(left - right) <= 1 && left != -1 && right != -1) {
return Math.max(left, right) + 1;
}
return -1;
}
}
本题AC受到上一题影响颇深,主要纠结的点是返回值为什么?该题解需要的返回值为Boolean类型,但是我们感觉我们的递归类型不能以Boolean来作为返回值,不然上层递归无法拿到下层递归返回的结果,无法判断上层是否为平衡二叉树。
由此想到了标记法,就比如我们返回的是平衡二叉树的最大深度,但如果该子树不是平衡二叉树,我们就做上标记,返回一个特殊的int类型的结果给上层(比如-1),上层判断如果下层返回-1,则直接返回给自己的上层-1,如不是,则正常就行判断自己是不是平衡二叉树,以此类推。
lc24:两两交换链表中的节点(中等)
题目描述:
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
面试题 02.07. 链表相交(简单)
题目描述:
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
图示两个链表在节点 c1 开始相交:
/**
* 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 = LenA_B(headA);
int lenB = LenA_B(headB);
ListNode curA = headA;
ListNode curB = headB;
while (lenA > lenB) {
curA = curA.next;
lenA--;
}
while (lenA < lenB) {
curB = curB.next;
lenB--;
}
while (curA != null) {
if (curA == curB) {
return curA;
}
curA = curA.next;
curB = curB.next;
}
return null;
}
public int LenA_B (ListNode head) {
ListNode cur = head;
int len = 0;
while (cur != null) {
len ++;
cur = cur.next;
}
return len;
}
}
解题心得:
总的来说这道题还是比较简单的,我们在需要链表长度时是可以遍历链表来求其长度的。
要求出两个链表相交的开始,我们知道相交之后的所有部分,他们的节点时相同的,就意味着我们需要尾对齐,更长的链表,链表前的部分肯定是不符合要求的,算了这个还是直接上图比较清晰。