1移除链表元素
给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
示例 1:
输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]
示例 2:
输入:head = [], val = 1 输出:[]
示例 3:
输入:head = [7,7,7,7], val = 7 输出:[]
提示:
- 列表中的节点数目在范围
[0, 104]
内 1 <= Node.val <= 50
0 <= val <= 50
思路:
使用虚拟头结点的方法来简化链表的删除操作。首先,创建一个值为0的虚拟头结点,然后将其指向原始链表的头部。接着,使用一个指针 cur
遍历整个链表,当 cur->next
的值等于目标值 val
时,删除当前节点的下一个节点,并将当前节点指向下下个节点;否则,将 cur
指针向后移动一个节点。最后,返回虚拟头结点的下一个节点作为新的链表头部。
代码:
class Solution {
public:
// 删除链表中值为 val 的所有节点
ListNode* removeElements(ListNode* head, int val) {
// 设置一个虚拟头结点
ListNode* dummyHead = new ListNode(0);
// 将虚拟头结点指向 head,这样方便后面做删除操作
dummyHead->next = head;
ListNode* cur = dummyHead;
// 遍历链表
while (cur->next != NULL) {
// 如果当前节点的下一个节点的值等于 val,则删除下一个节点
if(cur->next->val == val) {
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
} else {
cur = cur->next;
}
}
// 更新 head 指针
head = dummyHead->next;
// 删除虚拟头结点
delete dummyHead;
return head;
}
};
2设计链表
你可以选择使用单链表或者双链表,设计并实现自己的链表。
单链表中的节点应该具备两个属性:val
和 next
。val
是当前节点的值,next
是指向下一个节点的指针/引用。
如果是双向链表,则还需要属性 prev
以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。
实现 MyLinkedList
类:
MyLinkedList()
初始化MyLinkedList
对象。int get(int index)
获取链表中下标为index
的节点的值。如果下标无效,则返回-1
。void addAtHead(int val)
将一个值为val
的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。void addAtTail(int val)
将一个值为val
的节点追加到链表中作为链表的最后一个元素。void addAtIndex(int index, int val)
将一个值为val
的节点插入到链表中下标为index
的节点之前。如果index
等于链表的长度,那么该节点会被追加到链表的末尾。如果index
比长度更大,该节点将 不会插入 到链表中。void deleteAtIndex(int index)
如果下标有效,则删除链表中下标为index
的节点。
示例:
输入 ["MyLinkedList", "addAtHead", "addAtTail", "addAtIndex", "get", "deleteAtIndex", "get"] [[], [1], [3], [1, 2], [1], [1], [1]] 输出 [null, null, null, null, 2, null, 3] 解释 MyLinkedList myLinkedList = new MyLinkedList(); myLinkedList.addAtHead(1); myLinkedList.addAtTail(3); myLinkedList.addAtIndex(1, 2); // 链表变为 1->2->3 myLinkedList.get(1); // 返回 2 myLinkedList.deleteAtIndex(1); // 现在,链表变为 1->3 myLinkedList.get(1); // 返回 3
提示:
0 <= index, val <= 1000
- 请不要使用内置的 LinkedList 库。
- 调用
get
、addAtHead
、addAtTail
、addAtIndex
和deleteAtIndex
的次数不超过2000
。
思路:
这道题的要求有五个:
- 获取链表第index个节点的数值
- 在链表的最前面插入一个节点
- 在链表的最后面插入一个节点
- 在链表第index个节点前面插入一个节点
- 删除链表的第index个节点
-
定义节点结构体: 开始先定义了一个嵌套的节点结构体
LinkedNode
,它包含两个成员变量,一个是节点的值val
,另一个是指向下一个节点的指针next
。 -
初始化链表: 在
MyLinkedList
类的构造函数中,创建了一个虚拟头结点_dummyHead
,它的val
为0,next
为nullptr
,用来简化链表的操作。同时初始化了链表的大小_size
为0。 -
获取节点值:
get(int index)
函数用于获取链表中第index
个节点的值。通过判断index
是否合法,然后从虚拟头结点开始遍历找到目标节点,并返回其值。 -
在头部插入节点:
addAtHead(int val)
函数用于在链表的头部插入一个新节点。先创建一个新节点,然后将新节点的next
指向原头结点,再将虚拟头结点的next
指向新节点,最后更新链表大小。 -
在尾部插入节点:
addAtTail(int val)
函数用于在链表的尾部插入一个新节点。从虚拟头结点开始遍历找到最后一个节点,然后将其next
指向新节点,最后更新链表大小。 -
在指定位置插入节点:
addAtIndex(int index, int val)
函数用于在指定位置插入一个新节点。首先判断插入位置的合法性,然后从虚拟头结点开始遍历找到目标位置的前一个节点,插入新节点,并更新链表大小。 -
删除指定位置的节点:
deleteAtIndex(int index)
函数用于删除指定位置的节点。同样需要判断位置的合法性,然后找到目标位置的前一个节点,将其next
指针跳过待删除节点,删除节点并释放内存,最后更新链表大小。
代码:
class MyLinkedList {
public:
// 定义链表节点结构体
struct LinkedNode {
int val; // 节点的值
LinkedNode* next; // 指向下一个节点的指针
LinkedNode(int val):val(val), next(nullptr){}
//LinkedNode(int val) : val(val), next(nullptr) {}这行代码创建了一个LinkedNode结构体的构造函数,它接受一个整数参数val,并将val赋值给节点的val成员,同时将next指针初始化为nullptr。
};
// 初始化链表
MyLinkedList() {
_dummyHead = new LinkedNode(0); // 这里定义的头结点 是一个虚拟头结点,而不是真正的链表头结点
_size = 0;
}
// 获取到第index个节点数值,如果index是非法数值直接返回-1, 注意index是从0开始的,第0个节点就是头结点
int get(int index) {
if (index > (_size - 1) || index < 0) { // 判断index是否合法
return -1;
}
LinkedNode* cur = _dummyHead->next; // 从第一个真正的节点开始
while(index--) { // 循环找到第index个节点
cur = cur->next;
}
return cur->val; // 返回该节点的值
}
// 在链表最前面插入一个节点,插入完成后,新插入的节点为链表的新的头结点
void addAtHead(int val) {
LinkedNode* newNode = new LinkedNode(val); // 创建一个新节点
newNode->next = _dummyHead->next; // 将新节点的next指针指向原来的第一个节点
_dummyHead->next = newNode; // 将虚拟头结点指向新节点
_size++; // 更新链表长度
}
// 在链表最后面添加一个节点
void addAtTail(int val) {
LinkedNode* newNode = new LinkedNode(val); // 创建一个新节点
LinkedNode* cur = _dummyHead; // 从虚拟头结点开始遍历
while(cur->next != nullptr){ // 找到最后一个节点
cur = cur->next;
}
cur->next = newNode; // 将新节点加在最后一个节点之后
_size++; // 更新链表长度
}
// 在第index个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。
// 如果index 等于链表的长度,则说明是新插入的节点为链表的尾结点
// 如果index大于链表的长度,则返回空
// 如果index小于0,则在头部插入节点
void addAtIndex(int index, int val) {
if(index > _size) return; // 如果index大于链表长度,则直接返回
if(index < 0) index = 0; // 如果index小于0,则在头部插入节点
LinkedNode* newNode = new LinkedNode(val); // 创建一个新节点
LinkedNode* cur = _dummyHead; // 从虚拟头结点开始遍历
while(index--) { // 找到第index个节点的前一个节点
cur = cur->next;
}
newNode->next = cur->next; // 将新节点的next指针指向当前节点的next指针指向的节点
cur->next = newNode; // 将当前节点的next指针指向新节点
_size++; // 更新链表长度
}
// 删除第index个节点,如果index 大于等于链表的长度,直接return,注意index是从0开始的
void deleteAtIndex(int index) {
if (index >= _size || index < 0) { // 如果index大于等于链表长度或小于0,则直接返回
return;
}
LinkedNode* cur = _dummyHead; // 从虚拟头结点开始遍历
while(index--) { // 找到第index个节点的前一个节点
cur = cur ->next;
}
LinkedNode* tmp = cur->next; // 保存待删除节点
cur->next = cur->next->next; // 将待删除节点的前一个节点指向待删除节点的后一个节点
delete tmp; // 释放待删除节点的内存
tmp=nullptr; // 将指向待删除节点的指针置空,避免成为野指针
_size--; // 更新链表长度
}
private:
int _size; // 链表长度
LinkedNode* _dummyHead; // 虚拟头结点指针
};
3有效的字母异位词
给定两个字符串 s
和 t
,编写一个函数来判断 t
是否是 s
的字母异位词。
注意:若 s
和 t
中每个字符出现的次数都相同,则称 s
和 t
互为字母异位词。
示例 1:
输入: s = "anagram", t = "nagaram" 输出: true
示例 2:
输入: s = "rat", t = "car" 输出: false
提示:
1 <= s.length, t.length <= 5 * 104
s
和t
仅包含小写字母
思路:
-
记录字母出现次数: 首先,代码使用一个长度为26的整型数组
record
来记录每个字母出现的次数,数组的索引与字母的ASCII码对应,这样可以将小写字母映射到record
数组的相应位置。 -
遍历第一个字符串: 然后,代码遍历字符串
s
,对于每个字符,将其出现的次数记录在record
数组中相应的位置。 -
遍历第二个字符串: 接着,代码同样遍历字符串
t
,对于每个字符,将其出现的次数从record
数组中相应位置减去。 -
检查记录数组: 最后,代码遍历
record
数组,如果发现有任何一个位置的值不为0,说明两个字符串中有不同数量的某个字母,即它们不是字母异位词,返回false
;如果record
数组所有位置的值都为0,则说明两个字符串是字母异位词,返回true
代码:
class Solution {
public:
// 判断两个字符串是否是字母异位词
bool isAnagram(string s, string t) {
// 记录每个字母出现的次数的数组,初始值都为0
int record[26] = {0};
// 遍历字符串s,统计每个字母出现的次数
for (int i = 0; i < s.size(); i++) {
// 将字母映射到record数组的索引,统计出现次数
record[s[i] - 'a']++;
}
// 遍历字符串t,统计每个字母出现的次数
for (int i = 0; i < t.size(); i++) {
// 将字母映射到record数组的索引,减去出现次数
record[t[i] - 'a']--;
}
// 遍历record数组,如果有非零值,则说明不是字母异位词
for (int i = 0; i < 26; i++) {
if (record[i] != 0)
return false;
}
// 如果record数组所有元素都为0,则说明是字母异位词
return true;
}
};
4两个数组的交集
给定两个数组 nums1
和 nums2
,返回 它们的
交集
。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2] 输出:[2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4] 输出:[9,4] 解释:[4,9] 也是可通过的
提示:
1 <= nums1.length, nums2.length <= 1000
0 <= nums1[i], nums2[i] <= 1000
思路:
代码:
class Solution {
public:
// 求两个数组的交集
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
// 用于存储结果的无序集合
unordered_set<int> result_set;
// 将 nums1 转化为无序集合,方便进行查找
unordered_set<int> nums_set(nums1.begin(), nums1.end());
// 遍历 nums2,查找是否存在于 nums1 中,存在则加入结果集合
for (int num : nums2) {
if (nums_set.find(num) != nums_set.end()) {
result_set.insert(num);
}
}
// 将结果集合转化为数组并返回
return vector<int>(result_set.begin(), result_set.end());
}
};