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. [物理学与PDEs]第2章第2节 粘性流体力学方程组 2.2 应力张量

    1.  在有粘性的情形, 外界流体对 $\Omega$ 的作用力, 不仅有表面上的压力 (正压力), 也有表面上的内摩擦力 (切应力). 2.  于 $M$ 处以 ${\bf n}$ 为法向的单位面积 ...

  2. Consequence of Point-by-Point Bounds

    设 $X$ 是完备距离空间, $\scrF$ 是 $X$ 上的实连续函数族且具有性质: 对于每一 $x\in X$, 存在常数 $M_x>0$, 使得对于每一 $F\in\scrF$, $$\b ...

  3. EffectiveC++ 第5章 实现

    我根据自己的理解,对原文的精华部分进行了提炼,并在一些难以理解的地方加上了自己的"可能比较准确"的「翻译」. Chapter 5 实现 Implementations 适当提出属于 ...

  4. mui弹出菜单

    详细操作见代码: <!doctype html> <html> <head> <meta charset="UTF-8"> < ...

  5. 2018-2019-1 20165234 实现mypwd

    实现mypwd(选做,加分) 1 学习pwd命令 2 研究pwd实现需要的系统调用(man -k; grep),写出伪代码 3 实现mypwd 4 测试mypwd 提交过程博客的链接

  6. Pytorch报错记录

    1.BrokenPipeError 执行以下命令时: a,b = iter(train_loader).next() 报错:BrokenPipeError: [Errno 32] Broken pip ...

  7. 023_supervisorctl管理服务注意事项

    一. (1)问题 我在线上使用supervisorctl管理服务时遇到程序文件更新了,但是下次supervisorctl执行的时候并没有更新, 原因是supervisor更新后必须重新读取加载文件才行 ...

  8. 手把手教你写vue插件并发布(一)

    vue的插件开发 这篇文章较长,需要一定的阅读时间.这里有一份改善版本的插件笔记,在一个项目下完成开发.测试.发布的全过程.https://www.cnblogs.com/adouwt/p/96555 ...

  9. Mysql --数据库概述1

    什么是数据(Data)? 描述事物的符号记录称为数据,描述事物的符号既可以是数字,也可以是文字.图片,图像.声音.语言等,数据由多种表现形式,它们都可以经过数字化后存入计算机 在计算机中描述一个事物, ...

  10. Python 通用爬虫 和讯博客 scrapy

    目标站点需求分析 通用爬虫,获取和讯博客所有博文 涉及的库 scrapy,re,requests,mysql URL RULE 解析单页源码 保存到数据库 结果