目录
问题描述
设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val 和 next。val 是当前节点的值,next 是指向下一个节点的指针/引用。如果要使用双向链表,则还需要一个属性 prev 以指示链表中的上一个节点。假设链表中的所有节点都是 0-index 的。
在链表类中实现这些功能:
get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
示例:
MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1,2); //链表变为1-> 2-> 3
linkedList.get(1); //返回2
linkedList.deleteAtIndex(1); //现在链表是1-> 3
linkedList.get(1); //返回3
提示:
所有val值都在 [1, 1000] 之内。
操作次数将在 [1, 1000] 之内。
请不要使用内置的 LinkedList 库。
解题思路
这是一个比较基础的题,但是它考察了关于链表的一些基本操作,所以还算是比较重要的,如果在写这道题的过程中发现对一些特殊情况还是难以写清楚,建议多看看别人的代码,自己多写几遍。
由于我想考查一下自己的链表基础情况,所以我就普通的使用了一个单链表,没有使用双向链表。
这道题一共需要编写七个函数,以下将逐个进行讲解:
一
Creat函数对理解这个题是非常重要的,因为我们可以通过观察分析题意和函数形参得到这个函数是为了创建一个哨兵结点的。以下的所有函数的obj都是指向哨兵结点的指针,明白这一点可以少走很多弯路。
二
这里一定要注意了,这道题的节点规定是从0开始的,即若一共有n个有效节点(不包含哨兵结点),则下标是从0到n-1,可以认为哨兵结点下标为-1。
- get(index):获取链表中第
index
个节点的值。如果索引无效,则返回-1
。
get函数,像这种有可能会索引无效的情况,我个人的选择是先假设索引都是有效的情况,先把这种情况下的代码都写出来,然后根据索引无效的情况再增减条件和情况。
像这个get函数就是遍历链表,找到第index个节点即可。
然后特殊情况有好多种写法,看我下面的代码理解一下即可。
三
- addAtHead(val):在链表的第一个元素之前添加一个值为
val
的节点。插入后,新节点将成为链表的第一个节点。
这个也比较简单,直接在哨兵结点之后malloc一个值为val的节点就行啦。
四
- addAtTail(val):将值为
val
的节点追加到链表的最后一个元素。
和上一个一样的思路,只不过这个需要遍历到链表的最后一个然后再malloc一个新的。
五
addAtIndex(index,val):在链表中的第 index
个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
这个和六是一个类型的考虑方法,先考虑吧index为-1或0为临界点的情况,然后考虑正常情况下遍历链表的情况,最后再把index太大这种情况考虑进去特殊分析即可。
六
- deleteAtIndex(index):如果索引
index
有效,则删除链表中的第index
个节点。
同五。
七
简单,直接双指针遍历free即可
解题代码
typedef struct {
int val;
struct MyLinkedList* next;
} MyLinkedList;
//创建一个哨兵结点
MyLinkedList* myLinkedListCreate() {
MyLinkedList* head = (MyLinkedList*)malloc(sizeof(MyLinkedList));
head->next = NULL;
return head;
}
int myLinkedListGet(MyLinkedList* obj, int index) {
MyLinkedList* tail = obj;
int i = -1;
while (tail != NULL) {
if (i < index) {
tail = tail->next;
i++;
}
else if (i == index) {
return tail->val;
}
}
return -1;
}
void myLinkedListAddAtHead(MyLinkedList* obj, int val) {
MyLinkedList* p = (MyLinkedList*)malloc(sizeof(MyLinkedList));
p->val = val;
p->next = obj->next;
obj->next = p;
}
void myLinkedListAddAtTail(MyLinkedList* obj, int val) {
MyLinkedList* p = (MyLinkedList*)malloc(sizeof(MyLinkedList));
p->val = val;
p->next = NULL;
MyLinkedList* tail = obj;
while (tail->next != NULL) {
tail = tail->next;
}
tail->next = p;
}
void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) {
if (index <= 0) {
MyLinkedList* p = (MyLinkedList*)malloc(sizeof(MyLinkedList));
p->val = val;
p->next = obj->next;
obj->next = p;
}
else {
int i = -1;
MyLinkedList* tail = obj;
//一般我都是先写出正常情况下的add方式,然后再考虑特殊情况,如index无效
while (i < index - 1&&tail->next!=NULL) {
tail = tail->next;
i++;
}
if ((i == index - 1 && tail->next != NULL)||(i==index-1&&tail->next==NULL)) {
MyLinkedList* p = (MyLinkedList*)malloc(sizeof(MyLinkedList));
p->val = val;
p->next = tail->next;
tail->next = p;
}
}
}
void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
if (index < 0) {
return;
}
int i = 0;
MyLinkedList* p = obj;
MyLinkedList* q = obj->next;
//一般我都是先写出正常情况下的delete方式,然后再考虑特殊情况,如index无效
while (i < index&&q!=NULL) {
q = q->next;
p = p->next;
i++;
}
if (q != NULL) {
p->next = q->next;
free(q);
}
}
//将链表中所有结点都free很简单,只需像这样使用双指针逐个free即可
void myLinkedListFree(MyLinkedList* obj) {
MyLinkedList* p = obj;
MyLinkedList* q = p->next;
while (q != NULL) {
free(p);
p = q;
q = p->next;
}
free(p);
}
/**
* Your MyLinkedList struct will be instantiated and called as such:
* MyLinkedList* obj = myLinkedListCreate();
* int param_1 = myLinkedListGet(obj, index);
*
* myLinkedListAddAtHead(obj, val);
* myLinkedListAddAtTail(obj, val);
* myLinkedListAddAtIndex(obj, index, val);
* myLinkedListDeleteAtIndex(obj, index);
* myLinkedListFree(obj);
*/