链表篇(二)
设计链表
本题涉及到链表的基本操作:
链表节点值的获取,链表的插入(分为链表在表头插入和在表尾插入和指定位置插入)
题解——单链表
-
第一个要求——get()函数中,目的是获取链表中index个节点的值,先开始非常不理解这个while循环的工作原理,在文献查询之后,我明白了——
在链表中,index可视作一个计数器,告诉链表还有几次循环可以返回目标值,所以在index = 0 时跳出循环,cur不断向后迭代直到index = 0,完成查找。
-
第二个目标函数——addAtHead函数和addAtHead函数性质类似
- 对于
addAtHead
函数,只需要创建要插入的节点,而后替代虚拟头节点即可 - 对于
addAtTail
函数,多了一次遍历,及对于链表,在进行除头节点的插入、删除等操作时都需要遍历
- 对于
-
第三个目标函数——
addAtIndex(index,val)
即先将特殊情况去除——index超过链表长时返回为空。除此之外就是正常的插入操作。 -
第四个目标函数——
deleteAtIndex(index)
即先排除特殊情况,而后定义虚拟头节点后,对链表遍历,然后基础的删除链表节点操作。
代码:
class MyLinkedList {
public:
struct LinkNode{
int val;
LinkNode* next;
LinkNode(int val):val(val),next (NULL){}
};
MyLinkedList() {
dummyHead = new LinkNode(0);
size = 0;
}
int get(int index) {
if(index > (size - 1) || index < 0){
return -1;
}
LinkNode *cur = dummyHead->next;
while(index--){
cur = cur->next;
}
return cur->val;
}
void addAtHead(int val) {
LinkNode *newHeadNode = new LinkNode(val);
newHeadNode->next = dummyHead->next;
dummyHead->next = newHeadNode;
size++;
}
void addAtTail(int val) {
LinkNode* newTailNode = new LinkNode(val);
LinkNode* cur = dummyHead;
while(cur->next != NULL){
cur = cur->next;
}
cur->next = newTailNode;
size++;
}
void addAtIndex(int index, int val) {
if(index > size){
return;
}
LinkNode *newNode = new LinkNode(val);
LinkNode *cur =dummyHead;
while(index--){
cur = cur -> next;
}
newNode->next = cur->next;
cur->next = newNode;
size++;
}
void deleteAtIndex(int index) {
if (index >= size || index < 0){
return;
}
LinkNode *cur = dummyHead;
while(index--){
cur = cur->next;
}
LinkNode*tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
size--;
}
void PrintLinklist(){
LinkNode*cur =dummyHead;
while(cur->next != NULL){
cout << cur->next->val <<"";
cur = cur->next;
}
cout << endl;
}
private:
int size;
LinkNode *dummyHead;
};
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList* obj = new MyLinkedList();
* int param_1 = obj->get(index);
* obj->addAtHead(val);
* obj->addAtTail(val);
* obj->addAtIndex(index,val);
* obj->deleteAtIndex(index);
*/
感想
在纠正bug的过程中,发现以下几个错误:
- 对每一个新建链表节点时,忘记加
new
——即一个动态内存,可以将链表视为一个不断加长的数组。而new
相当于一个全局变量用来创建对象,但要记得释放内存。 - size随着函数要求“++”/“–”。
- NULL和nullptr的区别:
- 在C++中,NULL被定义为0,而nullptr可以用作空指针,可以有效的避免函数重载,而且nullptr可以转换成任何指针类型和bool类型,但不能转换为整数。
- 注意nullptr和NULL以及0在作为条件判断时值都为false,它们两两之间进行等于(==)判断时值为true
题解——双链表
- 对于在诸多题解中通用的一种格式——
Node(int val):val(val),next(nullptr){}
,该方式为构造函数的一种初始化内部成员方式,构造函数初始化列表——constructor
-参数表- 是类的列表初始值——只能初始化非静态数据成员。
- 对于双链表解题,可以想到的是在构造,以及链表的增删改查中会有指针的变化。
代码
struct Listnode{
int val;
struct Listnode *prev;
struct Listnode *next;
Listnode(): val(-1), prev(nullptr), next(nullptr) {}
Listnode(int v): val(v), prev(nullptr), next(nullptr){}
Listnode(int v, Listnode *p): val(v), prev(p), next(nullptr){}
Listnode(int v, Listnode *p, Listnode *n): val(v), prev(p), next(n){}
};
class MyLinkedList {
int size;
Listnode *head;
Listnode *tail;
public:
MyLinkedList(){
size = 0;
head = new Listnode(-1);
tail = new Listnode(-1);
head->next = tail;
tail->prev = head;
}
int get(int index) {
if(index < 0 || index >= size ){
return -1;
}
Listnode *tmp = head;
for(int i = 0; i<=index; i++){
tmp = tmp->next;
}
return tmp->val;
}
void addAtHead(int val) {
Listnode *newHeadNode = new Listnode(val,head,head->next);
newHeadNode->next->prev = newHeadNode;
newHeadNode->prev->next = newHeadNode;
size++;
}
void addAtTail(int val) {
Listnode *newTailNode = new Listnode(val,tail->prev,tail);
newTailNode->prev->next = newTailNode;
newTailNode->next->prev = newTailNode;
size++;
}
void addAtIndex(int index, int val) {
if(index > size){
return;
}
if(index == size){
addAtTail(val);
return;
}
if(index < 0){
addAtHead(val);
}
Listnode *tmp = head;
for(int i = 0; i<=index; i++){
tmp = tmp->next;
}
Listnode *NewNode = new Listnode(val, tmp->prev, tmp);
NewNode->prev->next = NewNode;
NewNode->next->prev = NewNode;
size++;
}
void deleteAtIndex(int index) {
if(index < 0 || index >= size){
return;
}
Listnode *tmp = head;
for(int i = 0; i<=index; i++){
tmp = tmp->next;
}
tmp->prev->next = tmp->next;
tmp->next->prev = tmp->prev;
delete tmp;
size--;
}
};
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList* obj = new MyLinkedList();
* int param_1 = obj->get(index);
* obj->addAtHead(val);
* obj->addAtTail(val);
* obj->addAtIndex(index,val);
* obj->deleteAtIndex(index);
*/
感想
在试运行双向链表时遇到了很多bug,在解决的过程中也有许多体会:
- 一个奇怪的规定——ListNode是C++里的一个固定的格式,不可以随便定义,因此换个定义名Listnode即可。
- 定义struct时,结尾加;
- while(index–)一共index个循环,但理应为index+1.因此for(int i = 0; i<=index; i++)符合要求。