提示:本题是系列LeetCode的150道高频题,你未来遇到的互联网大厂的笔试和面试考题,基本都是从这上面改编而来的题目
互联网大厂们在公司养了一大批ACM竞赛的大佬们,吃完饭就是设计考题,然后去考应聘人员,你要做的就是学基础树结构与算法,然后打通任督二脉,以应对波云诡谲的大厂笔试面试题!
你要是不扎实学习数据结构与算法,好好动手手撕代码,锻炼解题能力,你可能会在笔试面试过程中,连题目都看不懂!比如华为,字节啥的,足够让你读不懂题
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例:1
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
示例 2:
输入:head = [1], n = 1
输出:[]
示例 3:
输入:head = [1,2], n = 1
输出:[1]
提示:
链表中结点的数目为 sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这也很容易,就是双指针控制我cur指针走到末尾时,我pre指针在倒数第n个节点的前一个节点
啥意思呢?
先让cur走到n步,此时head头结点就是倒数第n个节点了,然后令pre来到头结点的前一个节点,
此后,cur走一步,pre走一步,同步走,两者间隔就是n+1个节点
最后cur走到null时,pre的下一个节点就是倒数第n个要删除的节点。
比如:
(1)最开始pre=null,
(2)cur从head开始走,n=2,n–,cur走,n–,cur走到2,n=0
(3)让n–=-1时,cur再挪一步到3,pre=head,看橘色
(4)然后pre先走,cur再走,粉色
(5)然后pre先走,cur再走,红色
(6)此时cur.next=null时,cur是最后一个节点,pre就是倒数第n=2个节点的前一个节点,pre跳指下下个节点5就完成删除任务
当然,有个特殊情况,如果倒数第n就是head呢?
就是说pre压根一直是null,那直接删除head,返回head.next即可
还有一个情况就是,压根你cur都没有走完n步,说明倒数第n个节点压根不存在,直接返回原来链表
中途呀,咱们统计一下count够不够n个点
懂了这个逻辑,实际上代码就是抠清楚边界
//复习:
//先让cur走到n步,此时head头结点就是倒数第n个节点了,然后令pre来到头结点的前一个节点,
//此后,cur走一步,pre走一步,同步走,两者间隔就是n+1个节点
//最后cur走到null时,pre的下一个节点就是倒数第n个要删除的节点。
public ListNode removeNthFromEndReview(ListNode head, int n) {
//让n复用,拿来控制pre和cur的走法,同时让count统计一下是否满了
boolean count = false;//用这个来标记好了
ListNode cur = head;
ListNode pre = null;
while (cur != null){
n--;//cur必须走
//每次走完要统计
if (n > 0) count = false;//count标记cur还没有走完n步
else if (n == 0) count = true;//此时cur刚刚好走完了n步,还需多走一步
else if (n == -1) pre = head;//此时cur多走了一步,pre指向倒数第n前一个
else pre = pre.next;//n<-1了,pre和count跟着走就行了
cur = cur.next;//先走完n步//不论如何cur都要往下走,所以我统一了
}
//从while出来,恰好pre就是倒数第n前一个
//分析,如果count压根没有走到n,说明节点不够,啥也不删除,返回原链表
if (count == false) return head;
//够了,但是要删除head的话
if (pre == null) return head.next;//pre恰好还没开始指到head
//如果pre不是空,就是删除pre.next
pre.next = pre.next.next;//跳指删除
return head;
}
分两拨测试:
长链
//造树
public static ListNode createList(){
ListNode head = new ListNode(1);
ListNode n2 = new ListNode(2);
ListNode n3 = new ListNode(3);
ListNode n4 = new ListNode(4);
ListNode n5 = new ListNode(5);
head.next = n2;
n2.next = n3;
n3.next = n4;
n4.next = n5;
return head;
}
public static void test(){
ListNode head = createList();
ListNode head2 = createList();
Solution solution = new Solution();
int n = 2;
ListNode res = solution.removeNthFromEnd(head, n);
while (res != null){
System.out.print(res.val + " ");
res = res.next;
}
System.out.println();
res = solution.removeNthFromEndReview(head2, n);
while (res != null){
System.out.print(res.val + " ");
res = res.next;
}
}
public static void main(String[] args) {
test();
}
测试问题不大
1 2 3 5
1 2 3 5
锻炼,恰好删除头结点
//造树
public static ListNode createList(){
ListNode head = new ListNode(1);
ListNode n2 = new ListNode(2);
// ListNode n3 = new ListNode(3);
// ListNode n4 = new ListNode(4);
// ListNode n5 = new ListNode(5);
head.next = n2;
// n2.next = n3;
// n3.next = n4;
// n4.next = n5;
return head;
}
2
2
n过长
//造树
public static ListNode createList(){
ListNode head = new ListNode(1);
ListNode n2 = new ListNode(2);
// ListNode n3 = new ListNode(3);
// ListNode n4 = new ListNode(4);
// ListNode n5 = new ListNode(5);
head.next = n2;
// n2.next = n3;
// n3.next = n4;
// n4.next = n5;
return head;
}
public static void test(){
ListNode head = createList();
ListNode head2 = createList();
Solution solution = new Solution();
int n = 3;
// ListNode res = solution.removeNthFromEnd(head, n);
// while (res != null){
// System.out.print(res.val + " ");
// res = res.next;
// }
System.out.println();
ListNode res = solution.removeNthFromEndReview(head2, n);
while (res != null){
System.out.print(res.val + " ");
res = res.next;
}
}
public static void main(String[] args) {
test();
}
返回整个链表
1 2
你也可以将链表转数组,控制下标搞定
也行
public ListNode removeNthFromEnd(ListNode head, int n) {
//一趟,省时间就要花空间
List<ListNode> list = new ArrayList<>();
ListNode cur = head;
int size = 0;
while (cur != null){
list.add(cur);
cur = cur.next;
size++;
}
//统计完成
if (size == n) return head.next;//这等价于删除头结点
if (size == 1) head = null;//只有一个头结点
else list.get(size - n - 1).next = list.get(size - n - 1).next.next;//剩下的删除都是换指针,jvm直接释放
return head;
}
提示:重要经验:
1)链表操作就是靠操控指针,往往都是双指针操作,要么pre,cur,要么fast,slow,都是这样的,搞清楚边界就行
2)删除普通链表好说,也是找pre和节点和cur,这种该删除倒数第n个,无非就是先让cur走n步,然后pre指向head的前一个节点,就是倒数第n个的前一个节点,跳指删除,中途注意看看是不是删除head,或者n过大,不需要删除
3)笔试求AC,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。