集度汽车一面算法:链表删除倒数第N个元素(要求时间复杂度是O(n))
集度汽车二面算法:
思路:
这两个题大体类似,考的都是链表的增删,需要注意的点:
1 边界问题
2 删除元素的方式
第一个算法题:
解法一:双指针
public ListNode deleteDuplicates(ListNode head) {
//定义第一次遍历元素的指针1
ListNode currentNode = head;
while(currentNode != null) {
//定义第二次遍历重复元素的指针2
ListNode nextNode = currentNode.next;
//当指针1和2的值相等的时候,指针2继续向后遍历,直到元素为null或者val不相等为止
while(nextNode != null && currentNode.val == nextNode.val) {
nextNode = nextNode.next;
}
//指针1指向不相等的元素,也就是指针2(相当于删除中间重复元素)
currentNode.next = nextNode;
//指针1指向指针2的原始位置,继续遍历
currentNode = nextNode;
}
return head;
}
解法二:递归
public ListNode deleteDuplicates2(ListNode head) {
//递归的终止条件之一(当前元素为空或者当前元素的next为空)
if(head == null || head.next == null){
return head;
}
//递归判断,当前节点的next节点
head.next = deleteDuplicates2(head.next);
//如果当前节点的val等于当前节点next节点的val,那么返回next作为递归的结果
if(head.val == head.next.val){
head = head.next;
}
return head;
}
第二道算法题:
第二道题是第一道题的变型,如果使用双指针,需要记录被删除元素的前一个节点
解法一:双指针1.0
//解法1 指针
public static ListNode deleteDuplicates1(ListNode head) {
ListNode preNode = new ListNode(0);
preNode.next = head;
//重新定义头结点(因为原始的头结点有可能变动)
ListNode newHead = null;
while(preNode.next != null){
//标示位: 判断有没有重复节点的删除
Boolean flag = false;
ListNode nextNode = preNode.next.next;
while(nextNode != null && preNode.next.val == nextNode.val){
flag = true;
nextNode = nextNode.next;
}
//如果有节点的删除,前指针直接越过重复元素,指向重复元素的后一个
if(flag){
preNode.next = nextNode;
}
//如果节点没有删除,前指针指向当前节点
else{
//判断最新头结点是否存在,如果不存在,创建新的头结点
//此时注意边界: 需要先与代码6 判断newHead == null, 因为如果后判断,preNode就已经移动到了preNode.next
if(newHead == null){
newHead = preNode.next;
}
//6
preNode = preNode.next;
}
}
return newHead;
}
解法二:官方版本(通过定义哑节点)
public static ListNode deleteDuplicates2(ListNode head) {
if(head == null) {
return head;
}
ListNode dummyNode = new ListNode(0,head);
//定义哑结点作为头部节点的前一个节点
ListNode currentNode = dummyNode;
while(currentNode.next != null && currentNode.next.next != null){
if(currentNode.next.val == currentNode.next.next.val){
//记录重复元素的值
int x = currentNode.next.val;
//删除重复元素
while(currentNode.next != null && currentNode.next.val == x){
currentNode.next = currentNode.next.next;
}
}else{
currentNode = currentNode.next;
}
}
return dummyNode.next;
}
解法三:递归版本
public static ListNode deleteDuplicates3(ListNode head) {
//没有节点或者只有一个节点,必然没有重复元素
if(head == null || head.next == null) {
return head;
}
// 当前节点与下一个节点的值重复了,重复的值都不能要。
// 一直往下找,找到不重复的节点。返回对不重复节点的递归结果
if(head.val == head.next.val) {
ListNode notDupNode = head.next.next;
while(notDupNode != null && notDupNode.val == head.val) {
notDupNode = notDupNode.next;
}
return deleteDuplicates3(notDupNode);
}
// 当前节点和下一个节点,值不同,则head的值是需要保留的,对head.next继续递归
else {
head.next = deleteDuplicates3(head.next);
return head;
}
}