一.链表的结构及分类
链表是一种通过指针串联组成的线性结构,每一个节点由两部分组成,分别为数据域(data)和指针域(next,存储下一个节点的指针地址),最后一个节点的指针域为NULL(空指针)。
链表的首个节点为头节点记作head,在链表进行各种增删,查询,遍历操作中会用到,也可以创建一个虚头节点,其指针域指向head来完成链表的各种操作(更加方便)。
链表分类
- 单向链表
- 双向链表
双向链表相对于单向链表,每个节点多了一个向前的指针域(prev),每个节点可以可以访问前后两个节点,表明双向链表既可以向前查询,也可以向后查询。
- 循环链表
循环链表,顾名思义,就是链表首尾相连。
循环链表可以用来解决约瑟夫环问题。
二.设计链表
链表的设计主要分为插入,删除,查询以及返回链表长度,插入方法包括头插法、尾插法和中间插入法,可以通过力扣707题来熟悉链表的设计操作。
- 头插法操作
将插入节点的指针域储存链表头节点。
void addAtHead(int val) {
LinkedNode *newnode=new LinkedNode(val);
newnode->next=dummyhead->next;//dummyhead为虚头节点
dummyhead->next=newnode;
size++;
}
- 尾插入操作
将尾部节点的指针域储存插入节点的指针。
void addAtTail(int val) {
LinkedNode *newnode=new LinkedNode(val);
LinkedNode *tmp=dummyhead;
while(tmp->next!=NULL)//遍历链表
{
tmp=tmp->next;
}
tmp->next=newnode;
size++;
}
- 中间插入
查询到需要插入位置的前一个位置记作tmp,首先将插入节点的指针域储存tmp下一个位置的地址,之后将tmp的指针域改为插入节点的地址。
遍历可以从头节点出发,通过循环,不断将节点改变为下一个节点来达到指定位置,循环index-1(插入位置)次后tmp的下一个节点为插入位置;也可以将tmp指向虚头节点,循环index(插入位置)次后tmp的下一个节点为插入位置。
// 在第index个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。
// 如果index 等于链表的长度,则说明是新插入的节点为链表的尾结点
// 如果index大于链表的长度,则返回空
// 如果index小于0,则在头部插入节点
void addAtIndex(int index, int val) {//index为插入的位置,链表位置重0开始
if(index>size)
return;
if(index < 0) index = 0;
LinkedNode *newnode=new LinkedNode(val);
LinkedNode *tmp=dummyhead;
while(index--)
{
tmp=tmp->next;
}
newnode->next=tmp->next;
tmp->next=newnode;
size++;
}
- 删除节点
查询到需要插入位置的前一个位置记作tmp,插入节点记作cur,第一步将tmp的储存域改为cur下一个节点的地址,然后删除cur。
注意删除cur后要将cur指向空指针,因为delete命令指示释放了cur指针原本所指的那部分内存,但并没有将其地址指向NULL,成为了野指针,因此需要将cur=NULL。
void deleteAtIndex(int index) {
if (index >= size || index < 0) {
return;
}
LinkedNode *tmp=dummyhead;
while(index--)
{
tmp=tmp->next;
}
LinkedNode *cur=tmp->next;
tmp->next=cur->next;
delete cur;
cur=NULL;
size--;
}
三.题目的代码
class MyLinkedList {
public:
struct LinkedNode{
int val;
LinkedNode* next;
LinkedNode(int x):val(x),next(NULL){}
};
MyLinkedList() {
dummyhead=new LinkedNode(0);
size=0;
}
int get(int index) {
if (index > (size - 1) || index < 0) {
return -1;
}
LinkedNode* cur = dummyhead->next;
while(index--){
cur = cur->next;
}
return cur->val;
}
void addAtHead(int val) {
LinkedNode *newnode=new LinkedNode(val);
newnode->next=dummyhead->next;
dummyhead->next=newnode;
size++;
}
void addAtTail(int val) {
LinkedNode *newnode=new LinkedNode(val);
LinkedNode *tmp=dummyhead;
while(tmp->next!=NULL)
{
tmp=tmp->next;
}
tmp->next=newnode;
size++;
}
void addAtIndex(int index, int val) {
if(index>size)
return;
if(index < 0) index = 0;
LinkedNode *newnode=new LinkedNode(val);
LinkedNode *tmp=dummyhead;
while(index--)
{
tmp=tmp->next;
}
newnode->next=tmp->next;
tmp->next=newnode;
size++;
}
void deleteAtIndex(int index) {
if (index >= size || index < 0) {
return;
}
LinkedNode *tmp=dummyhead;
while(index--)
{
tmp=tmp->next;
}
LinkedNode *cur=tmp->next;
tmp->next=cur->next;
delete cur;
cur=NULL;
size--;
}
void printLinkedList(){
LinkedNode*cur=dummyhead->next;
while(cur->next!=NULL)
{
cout<<cur->val<<" ";
cur=cur->next;
}
}
private:
int size;
LinkedNode* 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);
*/