数据结构day6
387. 字符串中的第一个唯一字符
思路解析:使用哈希表存储频数
383. 赎金信
思路解析:使用哈希表存储频数
242. 有效的字母异位词
思路解析:使用哈希表存储频数
141. 环形链表
给你一个链表的头节点 head ,判断链表中是否有环。
思路一:哈希表
思路二:快慢指针
21. 合并两个有序链表
思路一:递归
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if (l1 == NULL) {
return l2;
}
if (l2 == NULL) {
return l1;
}
if (l1->val <= l2->val) {
l1->next = mergeTwoLists(l1->next, l2);
return l1;
}
l2->next = mergeTwoLists(l1, l2->next);
return l2;
}
};
思路二:迭代
203. 移除链表元素
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
方法一:递归
方法二:迭代
方法三:双指针
方法四:虚拟头节点
思路解析
方法一:递归
链表的定义具有递归的性质,因此链表题目常可以用递归的方法求解。这道题要求删除链表中所有节点值等于特定值的节点,可以用递归实现。
对于给定的链表,首先对除了头节点 head 以外的节点进行删除操作,然后判断 head 的节点值是否等于给定的 val。如果 head 的节点值等于 val,则 head 需要被删除,因此删除操作后的头节点 head.next;如果 head 的节点值不等于 val,则 head 保留,因此删除操作后的头节点还是 head。上述过程是一个递归的过程。
递归的终止条件是 head 为空,此时直接返回 head。当 head 不为空时,递归地进行删除操作,然后判断 head 的节点值是否等于 val 并决定是否要删除 head。
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
if(head == NULL) {
return NULL;
}
head->next = removeElements(head->next,val);
return head->val==val ? head->next : head;
}
};
方法二:迭代
遍历链表找到所有值为 val 的节点;
将值为 val 的节点的上一个节点直接指向该节点的下一个节点( pre->next = pre->next->next)。
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
// update head if value equeal
while (head != NULL && head->val == val) {
head = head->next;
}
// return null if head is null
if (head == NULL) {
return NULL;
}
ListNode* pre = head;
while (pre->next != NULL) {
if (pre->next->val == val) {
pre->next = pre->next->next;
} else {
pre = pre->next;
}
}
return head;
}
};
方法三:双指针
设置两个指针分别指向头节点,pre (记录待删除节点的前一节点)和 cur (记录当前节点);
遍历整个链表,查找节点值为 val 的节点,找到了就删除该节点,否则继续查找。
找到,将当前节点的前一节点(之前最近一个值不等于 val 的节点)连接到当前节点(cur 节点)的下一个节点(pre->next = cur->next)。
没找到,更新最近一个值不等于 val 的节点(pre = cur),继续遍历(cur = cur->next)。
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
while (head != NULL && head->val == val) {
head = head->next;
}
ListNode* cur = head; // 当前节点
ListNode* pre = head; // 保存删除节点的前一节点
while (cur != NULL) {
if (cur->val == val) {
pre->next = cur->next;
} else {
pre = cur;
}
cur = cur->next;
}
return head;
}
};
方法四:虚拟头节点
之前的三种方法都需要考虑头节点的值是否为 val,并且在删除头节点的代码逻辑与删除当前节点的下一个节点的代码逻辑特别相似,可以进一步优化不用对头节点进行判断。
在头节点的前面增加虚拟头节点,这样头节点就跟其它节点一样,不需要单独拎出来考虑,但是需要注意的是返回的时候,需要返回虚拟头节点的下一节点而不是虚拟头节点。
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
// 创建虚拟头节点 智能指针,std::unique_ptr<ListNode> dummyHead(new ListNode(0));
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* cur = dummyHead;
while (cur->next != NULL) {
// 删除 cur->next
if (cur->next->val == val) {
ListNode* delNode = cur->next;
cur->next = delNode->next;
delete delNode;
} else {
cur = cur->next;
}
}
// 由于对虚拟头节点进行了 new,相应地就需要对它的空间进行释放,防止内存泄漏
ListNode* retNode = dummyHead->next;
delete dummyHead;
return retNode;
}
};