你可以选择使用单链表或者双链表,设计并实现自己的链表。
单链表中的节点应该具备两个属性: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
get
、addAtHead
、addAtTail
、addAtIndex
和 deleteAtIndex
的次数不超过 2000
。这题看上去十分复杂但将每一个函数拆开来看就不会很难了,但要注意运用链表时,节点的添加,指针的变化,虚拟头指针的运用,这是我做题时遇到的难点。
这题我们首先要定义一个链表出来,因为我们使用的是单指针,一个节点储存一个值和一个指针指向下一个节点,而要注意我们会得到struct ListNode*: 表示链表的一个节点,通常包含val和next;MyLinkedList: 表示一整个链表,包含了头指针head和长度size;代码如下
typedef struct {
struct ListNode *head;
int size;
} MyLinkedList;
然后我们需要书写一个可以创建新节点的函数,因为在后续解题中会持续使用,所以我们用函数把它包装起来,方便使用,这个函数就是先创建链表中的一个节点然后让它指向指定值,然后让它指向NULL。代码如下
struct ListNode *ListNodeCreat(int val) {
struct ListNode * node = (struct ListNode *)malloc (sizeof(struct ListNode));
node->val = val;
node->next = NULL;
return node;
}
第一行代码中用malloc函数申请了所需要的内存空间大小,但是因为是void类型的指针所以我们需要进行类型转换,转换为struct ListNode *类型。
第三步我们需要初始化链表,而且给他添加虚拟头指针,添加虚拟头指针也是为了方便后续书写,这样就不用单独计算头指针
MyLinkedList* myLinkedListCreate() {
MyLinkedList * obj = (MyLinkedList *)malloc(sizeof(MyLinkedList));
obj->head = ListNodeCreat(0);
obj->size = 0;
return obj;
}
第四步获取链表中下标为 index
的节点的值。如果下标无效,则返回 -1
。我们先判断index是否为负数或者是大于链表长度,是的话直接返回-1,如果不是,我们创造一个节点从头开始遍历数组直到index最后返回指针所对应的值。因为链表和数组不同,其是无序的,由指针的指向而连接,所以我们无法直接获得他指向的值吗,后面的函数大致都是这个思
最后一步使用完链表后我们需要释放它使用后的内存,先释放各个节点,然后释放链表管理结构
void myLinkedListFree(MyLinkedList* obj) {
struct ListNode *cur = NULL,*tmp = NULL;
for(cur = obj->head;cur;) {
tmp = cur;
cur = cur->next;
free(tmp);
}
free(obj);
}
#define MAX(a , b) ((a) > (b) ? (a) : (b))
typedef struct {
struct ListNode *head;
int size;
} MyLinkedList;
// 创建新节点
struct ListNode *ListNodeCreat(int val) {
struct ListNode * node = (struct ListNode *)malloc (sizeof(struct ListNode));
node->val = val;
node->next = NULL;
return node;
}
MyLinkedList* myLinkedListCreate() {
MyLinkedList * obj = (MyLinkedList *)malloc(sizeof(MyLinkedList));
obj->head = ListNodeCreat(0);
obj->size = 0;
return obj;
}
int myLinkedListGet(MyLinkedList* obj, int index) {
if(index < 0 || index >= obj->size) {
return -1;
}
struct ListNode *cur = obj->head;
for(int i=0;i<=index;i++) {
cur=cur->next;
}
return cur->val;
}
void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) {
if(index > obj->size) {
return;
}
index = MAX(0,index);
obj->size++;
struct ListNode *pred = obj->head;
for(int i=0;inext;
}
struct ListNode *toAdd = ListNodeCreat(val);
toAdd->next = pred->next;
pred->next = toAdd;
}
void myLinkedListAddAtHead(MyLinkedList* obj, int val) {
myLinkedListAddAtIndex(obj, 0,val);
}
void myLinkedListAddAtTail(MyLinkedList* obj, int val) {
myLinkedListAddAtIndex(obj, obj->size,val);
}
void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
if(index < 0|| index >=obj->size) {
return ;
}
obj->size--;
struct ListNode *pred = obj->head;
for(int i=0;inext;
}
struct ListNode *p = pred->next;
pred->next = pred->next->next;
free(p);
}
void myLinkedListFree(MyLinkedList* obj) {
struct ListNode *cur = NULL,*tmp = NULL;
for(cur = obj->head;cur;) {
tmp = cur;
cur = cur->next;
free(tmp);
}
free(obj);
}