Design your implementation of the linked list. You can choose to use the singly linked list or the doubly linked list. A node in a singly linked list should have two attributes: val and nextval is the value of the current node, and next is a pointer/reference to the next node. If you want to use the doubly linked list, you will need one more attribute prev to indicate the previous node in the linked list. Assume all nodes in the linked list are 0-indexed.

Implement these functions in your linked list class:

  • get(index) : Get the value of the index-th node in the linked list. If the index is invalid, return -1.
  • addAtHead(val) : Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list.
  • addAtTail(val) : Append a node of value val to the last element of the linked list.
  • addAtIndex(index, val) : Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. If index is negative, the node will be inserted at the head of the list.
  • deleteAtIndex(index) : Delete the index-th node in the linked list, if the index is valid.

Example:

MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1, 2); // linked list becomes 1->2->3
linkedList.get(1); // returns 2
linkedList.deleteAtIndex(1); // now the linked list is 1->3
linkedList.get(1);    // returns 3

Note:

  • All values will be in the range of [1, 1000].
  • The number of operations will be in the range of [1, 1000].
  • Please do not use the built-in LinkedList library.

这道题让我们实现一个链表的数据结构,说不能使用现成的链表数据结构,需要自己定义结点,说是可以实现双向或者单向的链表。既然有的选,那肯定选简单的单向链表了。首先就是要先自己定义一个结点的数据结构了,好在 LeetCode 中有很多很多的链表有关的题目,随便打开一个,并且参考一下结点的定义即可。然后看需要实现的哪些函数,分别是根据坐标取结点,在链表开头和末尾加结点,根据坐标位置加结点,根据坐标位置删除结点。既然是结点组成的链表,那么肯定不能向数组那样可以根据坐标直接访问元素,肯定至少要知道表头的位置。同时,在根据链表取结点函数说明了给定的位置可能是非法的,则也要知道链表中所有元素的个数,这样可以快速的判定给定的位置是否合法。

好,下面来看每个函数如何实现。首先来看根据坐标取结点函数,先判定 index 是否合法,然后从表头向后移动 index 个位置,找到要返回的结点即可。对于增加表头函数就比较简单了,新建一个头结点,next 连上 head,然后 head 重新指向这个新结点,同时 size 自增1。同样,对于增加表尾结点函数,首先遍历到表尾,然后在之后连上一个新建的结点,同时 size 自增1。下面是根据位置来加结点,肯定还是先来判定 index 是否合法,题目要求有过变动,新加一条说是当 index 为负数时,要在表头加个结点,这样的话只需要判断 index 是否大于 size 这一种非法情况。然后再处理一个 corner case,就是当 index 小于等于0的时候,直接调用前面的表头加结点函数即可。然后就是往后遍历 index-1 个结点,这里为啥要减1呢,因为要加入结点的话,必须要知道加入位置前面一个结点才行,链表加入结点的问题之前的题目中做过很多,这里就不说细节了,最后 size 还是要自增1。根据位置删除结点也是大同小异,没有太大的难度,参见代码如下:

解法一:

class MyLinkedList {
public:
MyLinkedList() {
head = NULL;
size = ;
}
int get(int index) {
if (index < || index >= size) return -;
Node *cur = head;
for (int i = ; i < index; ++i) cur = cur->next;
return cur->val;
}
void addAtHead(int val) {
Node *t = new Node(val, head);
head = t;
++size;
}
void addAtTail(int val) {
Node *cur = head;
while (cur->next) cur = cur->next;
cur->next = new Node(val, NULL);
++size;
}
void addAtIndex(int index, int val) {
if (index > size) return;
if (index <= ) {addAtHead(val); return;}
Node *cur = head;
for (int i = ; i < index - ; ++i) cur = cur->next;
Node *t = new Node(val, cur->next);
cur->next = t;
++size;
}
void deleteAtIndex(int index) {
if (index < || index >= size) return;
if (index == ) {
head = head->next;
--size;
return;
}
Node *cur = head;
for (int i = ; i < index - ; ++i) cur = cur->next;
cur->next = cur->next->next;
--size;
} private:
struct Node {
int val;
Node *next;
Node(int x, Node* n): val(x), next(n) {}
};
Node *head, *tail;
int size;
};

我们可以对上面的解法做一丢丢的优化,主要在末尾添加结点函数 addAtTail() 上,如果要大量调用这个方法的话,每次都要遍历到链表末尾,很不高效。于是可以同时记录表头和表尾的位置,这样就可以直接访问末尾结点了,缺点是在其他一些地方如果末尾元素改变了,要更新 tail 指针,否则就会出错,参见代码如下:

解法二:

class MyLinkedList {
public:
MyLinkedList() {
head = NULL; tail = NULL;
size = ;
}
int get(int index) {
if (index < || index >= size) return -;
Node *cur = head;
for (int i = ; i < index; ++i) cur = cur->next;
return cur->val;
}
void addAtHead(int val) {
Node *t = new Node(val, head);
head = t;
if (size == ) tail = t;
++size;
}
void addAtTail(int val) {
Node *t = new Node(val, NULL);
if (size == ) {tail = t; head = t;}
tail->next = t;
tail = t;
++size;
}
void addAtIndex(int index, int val) {
if (index > size) return;
if (index <= ) {addAtHead(val); return;}
if (index == size) {addAtTail(val); return;}
Node *cur = head;
for (int i = ; i < index - ; ++i) cur = cur->next;
Node *t = new Node(val, cur->next);
cur->next = t;
++size;
}
void deleteAtIndex(int index) {
if (index < || index >= size) return;
if (index == ) {
head = head->next;
--size;
return;
}
Node *cur = head;
for (int i = ; i < index - ; ++i) cur = cur->next;
cur->next = cur->next->next;
if (index == size - ) tail = cur;
--size;
} private:
struct Node {
int val;
Node *next;
Node(int x, Node* n): val(x), next(n) {}
};
Node *head, *tail;
int size;
};

最后来看一种很简洁的方法,用到了内置的双向队列 deque 这个数据结构,很难说这样算不算 cheating,但是巨简洁,大爱之~ 参见代码如下:

解法三:

class MyLinkedList {
public:
MyLinkedList() {} int get(int index) {
return (index >= && index < data.size()) ? data[index] : -;
}
void addAtHead(int val) {
data.push_front(val);
}
void addAtTail(int val) {
data.push_back(val);
}
void addAtIndex(int index, int val) {
if (index > (int)data.size()) return;
if (index <= ) { addAtHead(val); return; }
data.insert(data.begin() + index, val);
}
void deleteAtIndex(int index) {
if (index < || index >= data.size()) return;
data.erase(data.begin() + index);
} private:
deque<int> data;
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/707

参考资料:

https://leetcode.com/problems/design-linked-list/

https://leetcode.com/problems/design-linked-list/discuss/154116/C%2B%2B-deque

https://leetcode.com/problems/design-linked-list/discuss/145380/C%2B%2B-SOLUTION-24ms

https://leetcode.com/problems/design-linked-list/discuss/150999/C%2B%2B-simple-solution-beats-97.27!

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] Design Linked List 设计链表的更多相关文章

  1. 【LeetCode】Design Linked List(设计链表)

    这道题是LeetCode里的第707到题.这是在学习链表时碰见的. 题目要求: 设计链表的实现.您可以选择使用单链表或双链表.单链表中的节点应该具有两个属性:val 和 next.val 是当前节点的 ...

  2. Leetcode707.Design Linked List设计链表

    设计链表的实现.您可以选择使用单链表或双链表.单链表中的节点应该具有两个属性:val 和 next.val 是当前节点的值,next 是指向下一个节点的指针/引用.如果要使用双向链表,则还需要一个属性 ...

  3. LeetCode 707. Design Linked List (设计链表)

    题目标签:Linked List 题目让我们自己设计一个 linked list,可以是单向和双向的.这里选的是单向,题目并不是很难,但要考虑到所有的情况,具体看code. Java Solution ...

  4. [LeetCode] 141. Linked List Cycle 链表中的环

    Given a linked list, determine if it has a cycle in it. Follow up:Can you solve it without using ext ...

  5. [LeetCode] Design Circular Deque 设计环形双向队列

    Design your implementation of the circular double-ended queue (deque). Your implementation should su ...

  6. [LeetCode] Design Circular Queue 设计环形队列

    Design your implementation of the circular queue. The circular queue is a linear data structure in w ...

  7. [LeetCode] Design Phone Directory 设计电话目录

    Design a Phone Directory which supports the following operations: get: Provide a number which is not ...

  8. [LeetCode] Design Hit Counter 设计点击计数器

    Design a hit counter which counts the number of hits received in the past 5 minutes. Each function a ...

  9. [LeetCode] Design Snake Game 设计贪吃蛇游戏

    Design a Snake game that is played on a device with screen size = width x height. Play the game onli ...

随机推荐

  1. Teamviewer远程ssh命令行更改密码启动

    Teamviewer远程ssh命令行更改密码启动 设置密码 $ sudo teamviewer passwd [NewPasswd ] 查看teamviewer信息 $ teamviewer info ...

  2. [再寄小读者之数学篇](2014-05-27 矩阵的迹与 Jacobian)

    (from MathFlow) 设 $A=(a_{ij})$, 且定义 $$\bex \n_A f(A)=\sex{\cfrac{\p f}{\p a_{ij}}}. \eex$$ 试证: (1) $ ...

  3. shell1

    shebang = sharp-bang = #! 1.echo $PATH export PATH="$PATH:/home/user/bin" PREPEND prepend( ...

  4. make: 警告:检测到时钟错误。您的创建可能是不完整的。

    问题: make: 警告:检测到时钟错误.您的创建可能是不完整的. 原因:1. 文件时间不一致.(或者修改了系统时间) 如何解决:你touch *一下,然后重新编译 touch * touch命令有两 ...

  5. Arduino语言介绍

    Arduino语言介绍 Arduino语言是建立在C/C++基础上的,其基础是C语言,Arduino语言只不过把AVR单片机(微控制器)相关的一些参数设置都函数化,不用我们去了解他的底层,让不了解AV ...

  6. C++设计模式——原型模式

    什么是原型模式? 在GOF的<设计模式:可复用面向对象软件的基础>中是这样说的:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.这这个定义中,最重要的一个词是“拷贝”,也就 ...

  7. Alpha 冲刺 (9/10)

    目录 摘要 团队部分 个人部分 摘要 队名:小白吃 组长博客:hjj 作业博客:冲刺倒计时之9 团队部分 后敬甲(组长) 过去两天完成了哪些任务 答辩准备中 和大佬们跟进进度 接下来的计划 准备答辩 ...

  8. 树·AVL树/平衡二叉树

    1.AVL树 带有平衡条件的二叉查找树,所以它必须满足条件: 1 是一棵二叉查找树 2 满足平衡条件 1.1 平衡条件: 1)严格的平衡条件:每个节点都必须有相同高度的左子树和右子树(过于严格而不被使 ...

  9. 加密:HashUtils,RSAUtil,AESUtils

    import java.security.MessageDigest; public class HashUtils { public static String getMD5(String sour ...

  10. ILMerge参考文档

    ILMerge Michael BarnettResearch in Software Engineering (RiSE)Microsoft ResearchCopyright © Microsof ...