面试题 02.01. 移除重复节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* removeDuplicateNodes(ListNode* head) {
if(head == NULL){
return head;
}
set<int> si;
ListNode *pt = head, *nt = head;
si.insert(nt->val);
while(nt->next != NULL){ // 下一个节点还有值
if(si.find(nt->next->val) == si.end()){ // 且下一个节点不是重复的节点:将其值加入set,nt移动到下一个节点
si.insert(nt->next->val);
nt = nt->next;
}else{ // 且下一个节点是重复的节点:跳过下一个节点,nt指向下下个节点
nt->next = nt->next->next;
}
}
return pt;
}
};
面试题 02.02. 返回倒数第 k 个节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
int kthToLast(ListNode* head, int k) {
ListNode* fast = head, *slow = head;
for(int i = 0; i < k; ++i){
fast = fast->next;
}
while(fast != NULL){
fast = fast->next;
slow = slow->next;
}
return slow->val;
}
};
面试题 02.03. 删除中间节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
void deleteNode(ListNode* node) {
node->val = node->next->val; // 把下个节点的值偷过来
node->next = node->next->next; // 悄悄把毫无价值的下个节点删除
}
};
面试题 02.04. 分割链表
答案不唯一,即使输出与所给答案不同,提交依然有可能通过整个测试。
方法:双指针
cur是遍历的当前节点,pre为遍历中第一个未交换的节点。
cur向后遍历过程中,若遇到小于x的元素,就将cur的val与pre的val进行交换;否则,就一直往下遍历,直到链表尾。
例如: [3, 5, 8, 5, 10, 2, 1] 5 -> [3, 2, 1, 5, 10, 5, 8]。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
if(head == NULL){
return head;
}
ListNode *pre = head, *cur = head;
while(cur != NULL){
if(cur->val < x){
swap(pre->val, cur->val);
pre = pre->next;
}
cur = cur->next;
}
return head;
}
};
面试题 02.05. 链表求和
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode * head = new ListNode(-1);
ListNode * p1 = l1, * p2 = l2, * p = head;
int sum = 0, carr = 0; // 和,进位
while(p1 || p2 || carr){
sum = 0;
if(p1 != NULL){
sum += p1->val;
p1 = p1->next;
}
if(p2 != NULL){
sum += p2->val;
p2 = p2->next;
}
sum += carr;
ListNode * temp = new ListNode(sum % 10);
carr = sum / 10;
p->next = temp;
p = p->next;
}
return head->next;
}
};
面试题 02.06. 回文链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool isPalindrome(ListNode* head) {
// 使用快慢指针,慢指针在进行操作的时候,顺带的进行链表的翻转,在进行半个链表之间的比较
if(head == NULL){
return true;
}
ListNode* dummyNode = new ListNode(0);
dummyNode->next = head;
ListNode* slow = dummyNode;
ListNode* fast = dummyNode;
ListNode* prev = NULL;
// 使用快慢指针找到链表的中间位置,并翻转慢指针前的链表
while(fast != NULL && fast->next != NULL){
fast = fast->next->next;
// 链表翻转
ListNode* temp = slow->next;
slow->next = prev;
prev = slow;
slow = temp;
}
ListNode* rigth;
if(fast != NULL){
// 不为空,表示链表size为偶数
// 0 -> 1 -> 2 -> 3, prev指向1,slow指向2,要比较prev与right,先确定位置1与2的val是不是一样的
if(slow->val != slow->next->val){
return false;
}
// rigth从3开始
rigth = slow->next->next;
}
else{
// 为空,表示链表size为奇数
// 0 -> 1 -> 2 -> 3 -> 4, prev指向1,slow指向2
// rigth从3开始
rigth = slow->next;
}
// 比较两个链表
while(prev != NULL && rigth != NULL){
if(prev->val != rigth->val){
return false;
}
prev = prev->next;
rigth = rigth->next;
}
return true;
}
};
面试题 02.07. 链表相交
翻转链表后,从后往前找到最后一个相同的节点:但是这样修改了原来链表的状态
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode * preA = headA, *curA = headA->next;
preA->next = NULL;
while(curA != NULL){
ListNode* temp = curA->next;
curA->next = preA;
preA = curA;
curA = temp;
}
ListNode* preB = headB, *curB = headB->next;
preB->next = NULL;
while(curB != NULL){
ListNode* temp = curB->next;
curB->next = preB;
preB = curB;
curB = temp;
}
while(preA != NULL && preB != NULL){
if(preA != preB){
preA = preA->next;
preB = preB->next;
}else{
return preA;
}
}
return NULL;
}
};
使用set来记录链表1中的节点,再遍历链表2中的节点,与set中的数据进行比较:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
unordered_set<ListNode *> sl;
while(headA != NULL){
sl.insert(headA);
headA = headA->next;
}
while(headB != NULL){
if(sl.find(headB) != sl.end()){ // 等价于 if(sl.count(headB) == 1){
return headB;
}
headB = headB->next;
}
return NULL;
}
};
这种解法太强了:
这题应该是比较明显的双指针题,要是能实现一种算法让两个指针分别从A和B点往C点走,两个指针分别走到C后,又各自从另外一个指针的起点,也就是A指针第二次走从B点开始走,B指针同理,这样,A指针走的路径长度 AO + OC + BO 必定等于B指针走的路径长度 BO + OC + AO,这也就意味着这两个指针第二轮走必定会在O点相遇,相遇后也即到达了退出循环的条件。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode * t1 = headA, * t2 = headB;
while(t1 != t2){
if(t1 != NULL){
t1 = t1->next;
}else{
t1 = headB;
}
if(t2 != NULL){
t2 = t2->next;
}else{
t2 = headA;
}
}
return t1;
}
};
面试题 02.08. 环路检测
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(head == NULL || head->next == NULL){// 如果为空,或只有1个节点,必然不存在环
return NULL;
}
ListNode* fast = head, * slow = head; // 准备好快慢指针
while(fast != NULL && fast->next != NULL){// 快指针和快指针的下一个节点不为空时,才能一起行走
fast = fast->next->next;// 快指针走两步
slow = slow->next;// 慢指针走1步
if(fast == slow){ // 只要有环总会相遇,如果遇到fast为空,或fast的下一个节点为空还没相遇,说明就不存在环
break;
}
}
if(fast != slow){// 如果不是因为两者相等而退出while循环,说明是因为fast或fast->next节点为空而退出while循环,表明没有环
return NULL;
}
fast = head;// 到这里,说明fast = slow,此时,slow不动,让fast回到头
while(fast != slow){// 两者一步一步,相遇的地方就是环的入口
fast = fast->next;
slow = slow->next;
}
return fast;
}
};