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. DirectX11 With Windows SDK--19 模型加载:obj格式的读取及使用二进制文件提升读取效率

    前言 一个模型通常是由三个部分组成:网格.纹理.材质.在一开始的时候,我们是通过Geometry类来生成简单几何体的网格.但现在我们需要寻找合适的方式去表述一个复杂的网格,而且包含网格的文件类型多种多 ...

  2. MongoDB3.6 一键化自动部署方案

    1.系统基础配置 下面的命令默认都使用root用户进行操作,操作系统为Centos7,mongodb3.6.x以上版本 1.1 修改系统配置文件/etc/security/limits.conf和/e ...

  3. 第十一节:Bundles压缩合并js和css及原理分析

    一. 简介 1.背景:浏览器默认一次性请求的网络数是有上限的,如果你得js和css文件太多,就会导致浏览器需要多次加载,影响页面的加载速度, MVC中提供Bundles的方式压缩合并js和css,是M ...

  4. 第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)

    一. 简介 上一个章节中,也介绍了立即加载和延迟加载,但上一个章节是针对单表而言的,不含外键,立即也好,延迟也好,都是指单表中的数据.但本章节重点介绍的三种加载方式均是针对含(导航属性.外键)的情况下 ...

  5. go语言学习 一

    1.变量声明 指定变量类型,声明后若不赋值,使用默认值 根据值自行判定变量类型. 省略var, 注意 :=左侧的变量不应该是已经声明过的,否则会导致编译错误 2.go语言作用域 函数内定义的变量称为局 ...

  6. if判断中的true和false

    真值指的是在 布尔值 上下文中转换后(使用强制类型转换)的值为真的值.所有值都是真值,除非它们被定义为 falsy (即除了 false,0,"",null,undefined 和 ...

  7. Xpath初了解

    如下一段html: <html> <body> <form id="loginForm"> <input name="usern ...

  8. 【原创】大叔经验分享(17)编程实践对比Java vs Scala

    scala 官方地址 https://www.scala-lang.org/ 本文尽可能包含了一些主要的java和scala在编程实践时的显著差异,展现scala的代码的简洁优雅:scala通吃< ...

  9. poj 3278 搜索

    描述: Farmer John has been informed of the location of a fugitive cow and wants to catch her immediate ...

  10. 【python】随机数用法

    全文拷贝自:Python随机数用法 random.seed(int) 给随机数对象一个种子值,用于产生随机序列. 对于同一个种子值的输入,之后产生的随机数序列也一样. 通常是把时间秒数等变化值作为种子 ...