算法训练营 第三天
今天内容:链表01
链表基础
链表和数组的区别在于链表的内存是分开存储的,数组是连续的内存,因此数组的查找时间复杂度为O(1),链表需要从头开始找,查找时间复杂度为O(n),数组删除增加元素需要重新找一块内存空间,新建一个数组,将原数组搬过去,所以增删时间复杂度为O(n),链表直接改改指针,原地删除就行,所以增删时间复杂度为O(1).
203.移除链表元素
移除链表元素有两种方式,一种是用原链表操作,这种方式需要特殊处理head节点,因为head节点的处理方式和其他节点不一样,head的处理如下所示
while(head->val==val){
listNode* tmp = head;
head = head->next;
delete tmp;
}
第二种方法是添加一个虚拟头节点,这种方法可以保证head的处理方式和其他节点一样了,需要注意的是,这种方法返回时需要返回虚拟头节点的next(dH->next),因为原头节点可能被释放了。
listNode* dH = new listNode(0,head);
listNode* cur = head;
while(cur!=NULL && cur->next!=NULL){
if(cur->next->val==val){
listNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
}
else{
cur = cur->next;
}
}
return dH->next;
707. 设计链表
这道题用虚拟头节点是最好的,虚拟头节点的代码已经有很多了,我写了用原链表的设计方法
class MyLinkedList {
public:
MyLinkedList() {
head = NULL;
size = 0;
}
int get(int index) {
if (index >= size)
return -1;
ListNode* cur = head;
while (index--) {
cur = cur->next;
}
return cur->val;
}
void addAtHead(int val) {
ListNode* newHead = new ListNode(val, head);
head = newHead;
size++;
return;
}
void addAtTail(int val) {
if (size == 0) {
ListNode* newNode = new ListNode(val, NULL);
head = newNode;
size++;
return;
}
ListNode* cur = head;
int index = size - 1;
while (index--) {
cur = cur->next;
}
ListNode* newNode = new ListNode(val, NULL);
cur->next = newNode;
size++;
return;
}
void addAtIndex(int index, int val) {
if (index == size) {
addAtTail(val);
return;
}
if (index > size) {
return;
}
if (index == 0) {
addAtHead(val);
return;
}
ListNode* newNode = new ListNode(val, NULL);
ListNode* cur = head;
index--;
while (index--) {
cur = cur->next;
}
ListNode* tmp = cur->next;
cur->next = newNode;
newNode->next = tmp;
size++;
return;
}
void deleteAtIndex(int index) {
if (index >= size||index < 0)
return;
if (index == 0) {
ListNode* tmp = head;
head = head->next;
delete tmp;
size--;
return;
}
ListNode* cur = head;
index--;
while (index--) {
cur = cur->next;
}
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
size--;
return;
}
private:
ListNode* head;
int size;
};
这道题用原链表的方法需要做很多特殊情况处理,容易忽略一些情况,很难一次ac,在题目没有强调不能用虚拟头节点时还是用虚拟头节点方便一些,但原链表的方式也要会(之前参加某行笔试有要求不让用虚拟头节点)。
206. 反转链表
来了!经典的反转链表题,面试时被问烂了,hr的暖场神器!
这道题在两个月前刷过,但现在再看还是有些生疏,我将双指针法和递归法都实现了一遍,这道题怎么想到用双指针呢,因为这是单链表,要是反转链表肯定不能从后往前处理(后边的节点是无法得到前面节点信息的),从前往后的的话,就类似头插的方法完成反转(得到的第一个节点是尾节点,后边得到的所有节点都从前面插进新链表)
写完这道题,对递归的思想理解更深了一些,得注意return的是什么。