707.设计链表
题目链接:
题目描述
设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性: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 库。
思路:这道题需要掌握链表的基本操作。添加”虚拟头节点“能让首元节点的操作同非首元节点的操作一致。
代码(C++版本):
1、单链表
#include "iostream" using namespace std; //单链表 class MyLinkedList { public: struct LinkedNode { int val; LinkedNode *next; LinkedNode(int val) : val(val), next(NULL) {} }; private: LinkedNode* headNode; int size; public: //初始化链表 //如果链表带有一个虚拟头节点,那么首元节点的操作也跟其他节点的操作一致,否则要单独考虑对首元节点的操作 MyLinkedList() { headNode = new LinkedNode(0);//头节点为虚拟节点 size = 0; } //获取链表中第 index 个节点的值。如果索引无效,则返回-1。 //不包括头节点,首元节点的索引为0 int get(int index) { if (index < 0 || index > size - 1) { return -1; } int i = 0;//用于计数 LinkedNode* tp = headNode; while (tp->next != NULL) { if (i == index) { return tp->next->val; } else { i++; tp = tp->next; } } return -1; } //在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。 //头节点除外,第一个元素是首元节点 void addAtHead(int val) { LinkedNode* node = new LinkedNode(val); node->next = headNode->next; headNode->next = node; size += 1; } // 错误写法,这样会改变头指针所指的内容(在类中定义的函数,相对于对头节点加了&) // //将值为 val 的节点追加到链表的最后一个元素。 // void addAtTail(int val) { // LinkedNode* node = new LinkedNode(val); // while (headNode->next != NULL) { // headNode = headNode->next; // } // headNode->next = node; // size += 1; // } //将值为 val 的节点追加到链表的最后一个元素。 void addAtTail(int val) { LinkedNode* node = new LinkedNode(val); LinkedNode* tp = headNode; while (tp->next != NULL) { tp = tp->next; } tp->next = node; size += 1; } // 在链表中的第index个节点之前添加值为val的节点。 // 如果index等于链表的长度,则该节点将附加到链表的末尾。 // 如果 index 大于链表长度,则不会插入节点。 // 如果index小于0,则在头部插入节点。 void addAtIndex(int index, int val) { if (index == size ) { addAtTail(val); } else if (index > size) { return; } else if (index < 0) { addAtHead(val); } else { LinkedNode* node = new LinkedNode(val); LinkedNode* tp = headNode; for (int i = 0; i < index; i++) { tp = tp->next; } node->next = tp->next; tp->next = node; size += 1; } } //deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。 void deleteAtIndex(int index) { if (index >= 0 && index < size) { LinkedNode* tp = headNode; for (int i = 0; i < index; i++) { tp = tp->next; } LinkedNode* dp = tp->next; tp->next = tp->next->next; delete dp; size -= 1; } } //打印链表 void printLinkedList() { LinkedNode* tp = headNode; while (tp->next != NULL) { cout << tp->next->val << " "; tp = tp->next; } cout << endl; } }; int main() { MyLinkedList* linkedList = new MyLinkedList(); linkedList->addAtHead(1); linkedList->printLinkedList(); linkedList->addAtTail(3); linkedList->printLinkedList(); linkedList->addAtIndex(1, 2); //链表变为1-> 2-> 3 linkedList->printLinkedList(); int r1 = linkedList->get(1); //返回2 cout << r1 << endl; linkedList->deleteAtIndex(1); //现在链表是1-> 3 linkedList->printLinkedList(); int r2 = linkedList->get(1); //返回3 cout << r2 << endl; return 0; }
分析
-
时间复杂度: addAtHead: O(1) addAtInder,get,deleteAtIndex: O(k),其中 k 指的是元素的索引。 addAtTail:O(N),其中 N 指的是链表的元素个数。
-