学算法,刷力扣,加油卷,进大厂!
题目描述
力扣题目链接
设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性: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-> 3linkedList.get(1); //返回2linkedList.deleteAtIndex(1); //现在链表是1-> 3linkedList.get(1); //返回3
提示:
所有val值都在 [1, 1000] 之内。操作次数将在 [1, 1000] 之内。请不要使用内置的 LinkedList 库。
涉及算法
道题目属于中等题型,涉及到数据结构中的链表。这道题目呢,要求我们去根据要求设计链表,而不是单单让我们根据题目要求使用链表这个数据结构。那我们就要根据链表一些特性进行设计:
链表是由节点组成的,在链表的每个节点中,由两部分组成,即数据域(存放数据)和指针域(指向下一个节点的指针);链表不能像数组一样可以直接定位元素位置,因此它的查找比较慢;链表的删除和添加都是直接通过指针来添加的,因此不存在像数组一样的覆盖,这样就快了很多(如下图)
单链表
那么根据题目我们可以提炼出的关键点:
可以使用单链表或者双链表单链表中的两个属性值,val 是当前节点的值,next 是指向下一个节点的指针/引用;双链表的话,在此基础上添加一个属性,prev 以指示链表中的上一个节点
解决这道题目呢,我们需要做的就是根据前面说到的链表的特性结合题目要求,设计链表类,然后实现指定的操作就可以了。(本文使用单链表实现,双链表后续补充)
题目解答
Java题解一
使用单链表
使用单链表的话,需要给MyLinkedList 类中,添加一个虚拟的头结点,这样的话,方便题目要求的在链表的第一个位置添加;然后根据前面我们画的删除和添加的图发现,如果是删除元素,我们需要找到这个元素的前驱和后继节点;添加元素的话,需要根据其位置找到它的前驱节点和后继节点;为了方便链表的查找,我们给MyLinkedList 类中设置一个size属性,表示存储链表元素的个数。
根据以上分析,实现代码如下:
//定义单链表类class ListNode{int val; //当前节点的值ListNode next; //指向下一个节点的指针ListNode(){}ListNode(int val){this.val = val;}}class MyLinkedList {//size 存储链表元素的个数int size;//虚拟头结点ListNode head;//初始化链表public MyLinkedList() {size = 0;head = new ListNode(0);}//获取第index节点的数值public int get(int index) {//如果index非法,返回-1if(index < 0|| index >= size ){return -1;}//定义移动的节点ListNode curNode = head;//包含一个虚拟节点,所以查找第index +1 个节点for(int i = 0; i <= index; i++){curNode = curNode.next;}return curNode.val;}//在链表最前面插入一个节点public void addAtHead(int val) {addAtIndex(0,val);}//在链表最后面插入一个节点public void addAtTail(int val) {addAtIndex(size,val);}public void addAtIndex(int index, int val) {//排除index大于链表长和小于0的情况if(index > size ){return;}if(index < 0){index = 0;}//定义前驱节点ListNode pre = head;//链表长度加一++size;//循环查找直到indexfor(int i = 0; i < index; i++){pre = pre.next; //往后查找}//定义插入的节点addListListNode addList = new ListNode(val);//将节点插入addList.next = pre.next;pre.next = addList;}public void deleteAtIndex(int index) {//排除index大于链表长和小于0的情况if(index >= size || index < 0){return;}//定义前驱节点ListNode pre = head;//链表长度减一size--;//循环查找直到indexfor(int i = 0; i < index; i++){pre = pre.next; //往后查找}//将节点删除pre.next = pre.next.next;}}
复杂度分析
时间复杂度:
addAtHead: \O(1)addAtInder,get,deleteAtIndex: O(k),其中 k 指的是元素的索引。addAtTail:O(N),其中 N 指的是链表的元素个数。
空间复杂度:
所有的操作都是 O(1)。
如果觉得《卷进大厂系列之LeetCode刷题笔记:设计链表(中等)》对你有帮助,请点赞、收藏,并留下你的观点哦!