以前一直在CSDN发文,忽然在微信发文,好紧张!喜欢能够关注我哦!欢迎点个在看,点个赞,给点鼓励哦。web
1、线性表必知必懂的原理
(一)线性表通俗易懂原理
线性表是n个数据元素的有限序列,最经常使用的是链式表达
,一般也叫作线性链表或者链表
在链表中存储的数据元素也叫作结点
,一个结点存储的就是一条数据记录
。微信
每一个结点的结构包括两个部分:网络
一、具体的数据值;spa
二、指向下一个结点的指针。.net
在链表的最后一个结点,一般会有个头指针用来指向第一个结点3d
对于链表的最后一个结点,因为在它以后没有下一个结点,所以它的指针是个空指针
指针
假如:小明,小张,小李和小郑在排队卖鸡翅,可是排队就是讲究先来先买的原则,不能插队。小明在购买鸡翅付钱的时候忽然没钱了,假设小明和小郑认识,小明须要找到小郑借钱来付上买鸡翅的钱。。。那么小明想要找到小郑,就须要先找到他的下一个结点,一看不是小郑,就继续下一个结点,直到找到为止。code
小明是能够找到小郑的,可是小郑就没法找到小明了,也就是说单链表
反过来查找是不行的,也被称之为单向链表
。orm
为了弥补单向链表的不足
,咱们能够对单向链表进行改造:对象
对于单向链表,把最后一个元素的指针指向第一个元素,就获得了
循环链表
。这样小郑就能够查找他本身的next结点,就能够找到小明或者小李等。
因为单向链表一个结点只有指向下一个结点的指针,能够想象一下,是否是也能够修改为每一个结点也指向上一个结点,是否是就能够找到本身上一个结点的数据了呢。
咱们再增长一个指向上一个结点的指针,这样就获得了
双向链表
固然了,还有更好的办法,那就是把循环链表
和双向链表
进行结合
就获得了双向循环链表
。
不论是循环链表仍是双向链表,仍是双向循环链表,都是在单向链表的基础上加以改造获得的,改造后的链表在操做某种数据的时候能够提升必定的效率,因此单向链表是基础。
2、线性表对数据的操做
对数据的操做无非就是对数据的增删改查
。只是不一样的结构有着不一样的处理逻辑罢了。
(一)增长操做(老王插队神操做)
小明、小张、小李、小郑在排着队取票
,忽然来了一个老王,老王很是的着急,由于老王立刻就要到火车发车的时间了,只好插队了,此时老王跟后面全部的排队的人都打了个招呼,而后你们也都赞成他插队进去,那么老王就先去取票了,咱们忽略打招呼的过程,重点放在如何插队
。
咱们先无论如何插入到链表中的,先看图说话
。
老王若是想插队一定插入到小明的后面,由于老王在插队的过程当中小明此时可能会正在取票呢。
那么插入老王后的数据就是:
整个插入操做也很是的简单,只须要让老王的next指向小张,而后小明再指向老王就ok了。
思考一下????
反过来操做能够吗?
让小明先指向老王
,老王的next指向小张
。
能够吗?
答案是不能够的,这样的话,小明先指向老王
以后,小张以及小张以后的数据会所有丢失的。不信你看:
代码以下:
1newNode.next = head.next
2head.next = newNode
(二)删除操做(小明取完票让位给老王)
老王要想取到票,还欠一点火候,欠什么呢?须要等待小明取完票,而后离开才能够的。
那么就须要小明离开这个链表,咱们一般说删除小明这个结点。
删除操做也比较简单,只须要把前一个结点指向后面的后面的这个结点就ok了。
代码:(直接就能够忽略小明这个结点)
1head.next=head.next.next;
(三)查找操做
查找操做咱们一般会查两种,第一种是按照位置的序号,第二种是按照值来查找。
例如:
查找第3个位置的是谁;
查找小张是否还在排队。
在链表中的查找功能是比较弱的,对于链表中的查找,惟一的办法就是一个挨着一个的遍历去对比,对比较着去查找。
时间复杂度也就是O(N)。
3、单链表案例
(一)案例1:反转链表
一、题目描述
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
来源:力扣(LeetCode)
连接:https://leetcode-cn.com/problems/reverse-linked-list
著做权归领扣网络全部。商业转载请联系官方受权,非商业转载请注明出处。
二、解题思路
三、解题代码
1/**
2 * Definition for singly-linked list.
3 * public class ListNode {
4 * int val;
5 * ListNode next;
6 * ListNode(int x) { val = x; }
7 * }
8 */
9class Solution {
10 public ListNode reverseList(ListNode head) {
11
12 if(head==null)
13 return head;
14
15 //定义三个指针暂存后面的或者前面的结点,防止丢失。
16 ListNode curr,prev,next;
17 curr=head;
18 prev=next=null;
19
20 while(curr!=null){
21 next = curr.next;
22 curr.next = prev;
23 prev=curr;
24 curr=next;
25 }
26
27 return prev;
28 }
29}
若是不明白,能够本身画图来试试。
(二)案例2:找出链表的中间节点
一、题目描述
876. 链表的中间结点
难度简单256收藏分享切换为英文关注反馈
给定一个带有头结点 head
的非空单链表,返回链表的中间结点。
若是有两个中间结点,则返回第二个中间结点。
示例 1:
1输入:[1,2,3,4,5]
2输出:此列表中的结点 3 (序列化形式:[3,4,5])
3返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
4注意,咱们返回了一个 ListNode 类型的对象 ans,这样:
5ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
示例 2:
1输入:[1,2,3,4,5,6]
2输出:此列表中的结点 4 (序列化形式:[4,5,6])
3因为该列表有两个中间结点,值分别为 3 和 4,咱们返回第二个结点。
提示:
给定链表的结点数介于
1
和100
之间。
https://leetcode-cn.com/problems/middle-of-the-linked-list/
二、解题思路
思路很简单,就是利用两个指针来完成,一个走的比较快的,一个走的比较慢的。
每次快的走2个(next.next),每次慢的走1个节点(next)。
当快等于null时(next.next=null时),中止;
当快的next不为空时,返回慢的next节点。
当快的next为空时,返回慢的当前节点。
三、解题代码
1 //找出链表中间节点
2 static public ListNode middleNode(ListNode head) {
3
4 if(head==null)
5 return head;
6 ListNode fast,//快
7 slow;//慢
8 fast=slow=head;
9
10 while (fast!=null && fast.next!=null && fast.next.next!=null){
11 fast = fast.next.next;
12 slow = slow.next;
13 }
14
15 if(fast.next!=null)
16 return slow.next;
17
18 return slow;
19 }
(三)案例3:判断链表是否有环
一、题目描述
141. 环形链表
难度简单738收藏分享切换为英文关注反馈
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,咱们使用整数 pos
来表示链表尾链接到链表中的位置(索引从 0 开始)。若是 pos
是 -1
,则在该链表中没有环。
示例 1:
1输入:head = [3,2,0,-4], pos = 1
2输出:true
3解释:链表中有一个环,其尾部链接到第二个节点。
示例 2:
1输入:head = [1,2], pos = 0
2输出:true
3解释:链表中有一个环,其尾部链接到第一个节点。
示例 3:
1输入:head = [1], pos = -1
2输出:false
3解释:链表中没有环。
进阶:
你能用 O(1)(即,常量)内存解决此问题吗?
二、解题思路
此题也是能够利用快慢指针来完成的,一样,快指针一次走两步,慢指针一次走一步,若是有环的话,快和慢两个指针迟早会相遇的。那么若是快指针的val等于慢指针的val那么直接返回true便可。
三、解题代码
1/**
2 * Definition for singly-linked list.
3 * class ListNode {
4 * int val;
5 * ListNode next;
6 * ListNode(int x) {
7 * val = x;
8 * next = null;
9 * }
10 * }
11 */
12public class Solution {
13 public boolean hasCycle(ListNode head) {
14 if(head==null)
15 return false;
16
17 ListNode fast,slow;
18 fast=slow=head;
19 while (fast!=null && fast.next!=null && fast.next.next!=null){
20 fast=fast.next.next;
21 slow=slow.next;
22 if(fast.val==slow.val)
23 return true;
24 }
25
26 return false;
27 }
28}
4、链表总结
链表对数据的存储方式是按照顺序的存储。
何时用?
当数据元素不肯定时。
当须要常常进行数据的新增和删除时。
链表的反转、快慢指针是高效的操做链表的主要方法,是必需要掌握的内容。
本文分享自微信公众号 - TrueDei(monkeystudy)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。