关于链表:单链表知识点

单链表1.程序表 长处:物理空间间断,反对随机拜访 毛病:空间不够就须要扩容,破费工夫和空间;插入删除效率低下 2.单链表 长处:按需申请开释空间;插入删除常数工夫 毛病:不反对随机拜访 3.留神点 (1)在批改指针自身的内容时,也就是扭转指针自身存储的地址,咱们须要的是二级指针 void list_push_back(struct node** head, type x){ struct node* newnode = (struct node*)malloc(sizeof(struct node)); assert(newnode); newnode->data = x; newnode->next = NULL; //这里想扭转头结点的地址,让它指向一块新的空间,也就是扭转一个*类型的值->于是咱们须要**作为形式参数!! if (*head == NULL) { *head = newnode; } else { struct node* cur = *head; while (cur->next != NULL) { cur = cur->next; } cur->next = newnode; }}4.实现 //forward_list<int> s;外面的插入删除:insert_after erase_aftervoid list_print(struct node* head){ struct node* cur = head; while (cur != NULL) { printf("%d ", cur->data); cur = cur->next; } printf("\n");}struct node* buy_newnode(type x){ struct node* newnode = (struct node*)malloc(sizeof(struct node)); assert(newnode); newnode->data = x; newnode->next = NULL; return newnode;}void list_push_back(struct node** phead, type x){ assert(phead); struct node*newnode = buy_newnode(x); //这里想扭转头结点的地址,让它指向一块新的空间,也就是扭转一个*类型的值->于是咱们须要**作为形式参数!! if (*phead == NULL) { *phead = newnode; } else { struct node* cur = *phead; while (cur->next != NULL) { cur = cur->next; } cur->next = newnode; }}//也须要传入二级指针:不论有没有元素,都须要扭转head的值,因为head必须是指向第一个结点的!!! void list_push_front(struct node** phead, type x){ assert(phead); struct node* newnode = buy_newnode(x); newnode->next = *phead; *phead = newnode; //验证高低都能够但下面的更加简洁 /*if (*phead == NULL) { *phead = newnode; } else { newnode->next = *phead; *phead = newnode; }*/}//一级指针不必查看:它可能就是一个空链表//二级指针须要查看:head是存在的,head有这个变量,那就肯定有个地址。phead外面寄存的就是head的地址void list_pop_front(struct node**phead){ assert(phead); assert(*phead);//这里头删,head肯定须要有值才能够删除 struct node* pre_head = *phead; *phead = (*phead)->next; free(pre_head);//开释它指向的空间}void list_pop_back(struct node**phead){ assert(phead); assert(*phead); struct node* t = *phead; if (t->next == NULL) { free(*phead); *phead = NULL; } else { while (t->next->next != NULL) { t = t->next; } t->next = NULL; free(t->next); } }struct node* list_find(struct node* head, type x){ assert(head); struct node* t = head; while (t != NULL) { if (t->data == x) { return t; } t = t->next; } return NULL;}void list_insert(struct node** phead, struct node* pos, type x){ assert(pos); assert(phead); if (pos == *phead) { list_push_front(phead, x); } else { struct node* t = *phead; while (t->next != pos) { t = t->next; } struct node*newnode = buy_newnode(x); newnode->next = pos; t->next = newnode; }}void list_erase(struct node**phead, struct node *pos){ assert(phead); assert(pos);//如果链表为空,pos不可能无效 if (pos == *phead) { list_pop_front(phead); } else { struct node* t = *phead; while (t->next != pos) { t = t->next; } t->next = pos->next; free(pos); //pos = NULL;//简直有效?因为pos是个形参,在这里置空无用,交给里面解决 }}void list_insert_after(struct node* pos, type x){ assert(pos); struct node * newnode = buy_newnode(x); newnode->next = pos->next; pos->next = newnode;}void list_erase_after(struct node* pos){ assert(pos->next); struct node* t = pos->next; pos->next = pos->next->next; free(t);}

May 16, 2023 · 2 min · jiezi

关于链表:链式前向星介绍以及原理

1 链式前向星1.1 简介链式前向星可用于存储图,实质上是一个动态链表。 一般来说,存储图常见的两种形式为: 邻接矩阵邻接表邻接表的实现个别应用数组实现,而链式前向星就是应用链表实现的邻接表。 1.2 出处出处可参考此处。 2 原理链式前向星有两个外围数组: pre数组:存储的是边的前向链接关系last数组:存储的是某个点最初一次呈现的边的下标感觉云里雾里对吧,能够看看上面的具体解释。 2.1 pre数组pre数组存储的是一个链式的边的前向关系,下标以及取值如下: pre数组的下标:边的下标pre数组的值:前向边的下标,如果没有前向边,取值-1这里的前向边是指,如果某个点,作为起始点,曾经呈现过边x,那么,遍历到以该点作为起始点的下一条边y时,边y的前向边就是边x。 更新pre数组的时候,会遍历每一条边,更新该边对应的前向边。 比方,输出的有向边如下: n=6 // 顶点数[[0,1],[1,3],[3,5],[2,4],[2,3],[0,5],[0,3],[3,4]] // 边那么: 对于第一条边,下标为0,那么会更新pre[0]的值,边为0->1,而起始点点0还没有呈现过前向边,那么pre[0]=-1。这样就建设了边0->-1的一个链接关系,也就是说,对于起始点点0,它只有边0这一条边对于第二条边,下标为1,那么会更新pre[1]的值,边为1->3,而起始点点1还没有呈现过前向边,那么pre[1]=-1。这样就建设了边1->-1的一个链接关系,也就是说,对于起始点点1,它只有边1这一条边对于第三条边,下标为2,那么会更新pre[2]的值,边为3->5,而起始点点3还没有呈现过前向边,那么pre[2]=-1。这样就建设了边2->-1的一个链接关系,也就是说,对于起始点点3,它只有边2这一条边对于第四条边,下标为3,那么会更新pre[3]的值,边为2->4,而起始点点2还没有呈现过前向边,那么pre[3]=-1。这样就建设了边3->-1的一个链接关系,也就是说,对于起始点点2,它只有边3这一条边对于第五条边,下标为4,那么会更新pre[4]的值,边为2->3,而起始点点2,曾经呈现过一条边了,该边的下标是3,也就是前向边为3,那么就会更新pre[4]为前向边的值,也就是pre[4]=3。这样,就建设了边4->3->-1的一个链接关系,也就是对于起始点点2来说,目前有两条边,一条是边4,一条是边3对于第六条边,下标为5,那么会更新pre[5]的值,边为0->5,而起始点点0,曾经呈现过一条边了,该边的下标是边0,也就是前向边为0,那么就会更新pre[5]为前向边的值,也就是pre[5]=0。这样,就建设了边5->0->-1的一个链接关系,也就是对于起始点点0来说,目前有两条边,一条是边5,一条是边0对于第七条边,下标为6,那么会更新pre[6]的值,边为0->3,而起始点点0,曾经呈现过不止一条边了,最初一次呈现的边为边5,也就是前向边为5,那么就会更新pre[6]为前向边的值,也就是pre[6]=5。这样,就建设了边6->5->0->-1的一个链接关系,也就是对于起始点点0来说,曾经有三条边了,一条是边6,一条是边5,一条是边0对于第八条边,下标为7,那么会更新pre[7]的值,边为3->4,而起始点点3,曾经呈现过一条边了,该边的下标是边2,也就是前向边为2,那么就会更新pre[7]为前向边的值,也就是pre[7]=2。这样,就建设了边7->2->-1的一个链接关系,也就是对于起始点点3来说,目前有两条边,一条是边7,一条是边2这样,边的链接关系就建设下来了: 点 边的链接关系(边的下标)0 6->5->0->-11 1->-12 4->3->-13 7->2->-14 -15 -12.2 last数组last数组存储的是最初一次呈现的前向边的下标,下标以及取值如下: last数组的下标:点last数组的值:最初一次呈现的前向边的下标last数组会将所有值初始化为-1,示意所有的点在没有遍历前都是没有前向边的。 应用下面的数据举例: n=6 // 顶点数[[0,1],[1,3],[3,5],[2,4],[2,3],[0,5],[0,3],[3,4]] // 边last数组会与pre数组一起在遍历边的时候更新: 遍历到第一条边:下标为0,边为0->1,那么会更新以0为起始点的前向边的值,也就是本人,last[0]=0。而后,如果下一次遍历到了以0为起始点的边,比方0->5,那么0->5的前向边就是边0,而边0就存储在last[0]中,下次须要的时候间接取last[0]即可遍历到第二条边:下标为1,边为1->3,那么会更新以1为起始点的最初一次呈现的前向边的值,也就是last[1]=1遍历到第三条边:下标为2,边为3->5,那么会更新以3为起始点的最初一次呈现的前向边的值,也就是last[3]=2遍历到第四条边:下标为3,边为2->4,那么会更新以2为起始点的最初一次呈现的前向边的值,也就是last[2]=3遍历到第五条边:下标为4,边为2->3,那么会更新以2为起始点的最初一次呈现的前向边的值,也就是last[2]=4遍历到第六条边:下标为5,边为0->5,那么会更新以0为起始点的最初一次呈现的前向边的值,也就是last[0]=5遍历到第七条边:下标为6,边为0->3,那么会更新以0为起始点的最初一次呈现的前向边的值,也就是last[0]=6遍历到第八条边:下标为7,边为3->4,那么会更新以3为起始点的最初一次呈现的前向边的值,也就是last[3]=7在遍历每条边的时候,会先从last数组取值并赋给pre去生成链接关系,而后更新last数组中对应起始点的值为以后的边的下标。 3 代码3.1 生成数组生成last以及pre数组: public class Solution { private int[] pre; private int[] last; private void buildGraph(int n, int[][] edge) { int edgeCount = edge.length; pre = new int[edgeCount]; last = new int[n]; Arrays.fill(last, -1); for (int i = 0; i < edgeCount; i++) { int v0 = edge[i][0]; pre[i] = last[v0]; last[v0] = i; } }}pre的范畴与边数无关,而last的范畴与点数无关。一开始须要初始化last数组为-1,而后遍历每一条边: ...

February 19, 2023 · 3 min · jiezi

关于链表:Day-67100-数据结构链表8奇偶链表

(一)需要今儿还是链表中的一个算法——奇偶链表。 (二)奇偶链表1、问题形容给定单链表的头节点 head ,将所有索引为奇数的节点和索引为偶数的节点别离组合在一起,而后返回从新排序的列表。 第一个节点的索引被认为是 奇数 , 第二个节点的索引为 偶数 ,以此类推。 请留神,偶数组和奇数组外部的绝对程序应该与输出时保持一致。 你必须在 O(1) 的额定空间复杂度和 O(n) 的工夫复杂度下解决这个问题。 Demo1: 输出: head = [1,2,3,4,5]输入: [1,3,5,2,4]Demo2: 输出: head = [2,1,3,5,6,4,7]输入: [2,3,6,7,1,5,4]2、思路:定义一个新的额定空间一一遍历原链表将合乎奇偶数据的节点,放到对应申明的变量中 3、代码/** * Definition for singly-linked list. * class ListNode { * val: number * next: ListNode | null * constructor(val?: number, next?: ListNode | null) { * this.val = (val===undefined ? 0 : val) * this.next = (next===undefined ? null : next) * } * } */function oddEvenList(head: ListNode | null): ListNode | null { if(head === null){ return head } let evenHead = head.next; // 定义偶数头结点 let odd = head // 定义奇数头结点 let even = evenHead // 定义偶数头结点 while(even != null && even.next!=null){//去除不成奇偶对 odd.next = even.next // 偶数的下一个,给了奇数初始头的下一个 odd = odd.next // 奇数节点右移一位 even.next = odd.next //奇数的下一个是偶数,给了新开队列的下一个 even = even.next // 偶数节点右移一位 } odd.next = evenHead // 奇数节点的开端的下一个,是偶数节拍板结点 return head};空间复杂度将是 O(1)工夫复杂度将是 O(n)以上 ...

May 18, 2022 · 1 min · jiezi

关于链表:Day-65100-数据结构链表6移除链表中的某个节点

(一)需要今儿持续是链表构造的算法——移除链表中的某个节点 (二)移除链表中的某个节点1、问题形容给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。 Demo1: 输出:head = [1,2,6,3,4,5,6], val = 6输入:[1,2,3,4,5]Demo2: 输出:head = [], val = 1输入:[]Demo3: 输出:head = [7,7,7,7], val = 7输入:[]2、思路:定义一个头节点;迭代节点;判断值,如果值相等,那么把下一个节点的next赋值给以后节点的next值;3、代码/** * Definition for singly-linked list. * function ListNode(val, next) { * this.val = (val===undefined ? 0 : val) * this.next = (next===undefined ? null : next) * } *//** * @param {ListNode} head * @param {number} val * @return {ListNode} */var removeElements = function(head, val) { // 缺一个头节点,就定义一个 let prev = new ListNode(0) // 便于解决,首节点的值就雷同的状况 prev.next = head let curr = prev // 从新定义一个头节点 while(curr.next!=null){ const next = curr.next // 下一个节点 if (next.val == val){ // 下一个节点的值和目标值相等 curr.next = next.next // 把下一个节点的next赋值给以后节点的next值 }else{ curr = curr.next } } return prev.next};空间复杂度将是 O(1)工夫复杂度将是 O(n)以上 ...

May 16, 2022 · 1 min · jiezi

关于链表:Day-63100-数据结构链表5用双指针法删除链表倒数节点

(一)需要双指针解决链表的问题,还是蛮有意思~ 持续做~ (二)用双指针法删除链表倒数节点1、问题形容给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。 输出:head = [1,2,3,4,5], n = 2输入:[1,2,3,5]2、思路:定义快慢指针,快指针挪动n个地位;fast 和 slow 同时挪动,fast挪动到最初一个节点时,slow的下一个节点就是要删除的节点3、题目/** * Definition for singly-linked list. * function ListNode(val, next) { * this.val = (val===undefined ? 0 : val) * this.next = (next===undefined ? null : next) * } *//** * @param {ListNode} head * @param {number} n * @return {ListNode} */var removeNthFromEnd = function(head, n) { let fast = head // 定义快指针 let slow = head // 定义慢指针 // 快指针挪动n个地位 while(n--){ fast = fast.next } // 如果fast为null 那么阐明删除的是第一个节点,返回第二个节点就OK if(!fast){ head = head.next return head } // fast 和 slow 同时挪动,fast挪动到最初一个节点时,slow的下一个节点就是要删除的节点 while(fast.next){ fast = fast.next slow = slow.next } slow.next = slow.next.next return head};空间复杂度将是 O(1)工夫复杂度将是 O(n)以上 ...

May 12, 2022 · 1 min · jiezi

关于链表:算法链表

哨兵节点哨兵节点是为了简化解决链表边界条件而引入的附加链表节点。哨兵节点通常位于链表的头部,它的值没有任何意义。在一个有哨兵节点的链表中,从第2个节点开始才真正保留有意义的信息。 用哨兵节点简化链表插入操作用哨兵节点简化链表删除操作应用哨兵节点能够简化创立或删除链表头节点操作的代码。 双指针删除倒数第k个节点题目:如果给定一个链表,请问如何删除链表中的倒数第k个节点?假如链表中节点的总数为n,那么1≤k≤n。要求只能遍历链表一次。/** * @param {ListNode} head * @param {number} n * @return {ListNode} */var removeNthFromEnd = function(head, n) { const dummy = new ListNode(0, head); let front = head, back = dummy; for(let i = 0;i<n;i++) { front = front.next } while(front !== null) { front = front.next; back = back.next } back.next = back.next.next return dummy.next};链表中环的入口节点题目:如果一个链表中蕴含环,那么应该如何找出环的入口节点?从链表的头节点开始顺着next指针方向进入环的第1个节点为环的入口节点。例如,在如图4.3所示的链表中,环的入口节点是节点3。反转链表反转链表题目:定义一个函数,输出一个链表的头节点,反转该链表并输入反转后链表的头节点var reverseList = function(head) { const prev = null; const cur = head; while(cur != null) { const next = cur.next; cur.next = prev; prev = cur; cur = next; } return prev}链表中的数组相加题目:给定两个示意非负整数的单向链表,请问如何实现这两个整数的相加并且把它们的和依然用单向链表示意?链表中的每个节点示意整数十进制的一位,并且头节点对应整数的最高位数而尾节点对应整数的个位数。剖析:要思考整数溢出的状况。 ...

April 1, 2022 · 3 min · jiezi

关于链表:完虐算法合并两个有序链表

合并两个有序链表LeetCode21. 合并两个有序链表 问题形容输出两个枯燥递增的链表,输入两个链表合成后的链表,咱们须要合成后的链表满足枯燥不减规定。 示例: 输出: {1,3,5},{2,4,6} 返回值: {1,2,3,4,5,6} 剖析问题既然给定的两个链表都是有序的,那么咱们能够判断两个链表的头结点的值的大小,将较小值的结点增加到后果中,而后将对应链表的结点指针后移一位,周而复始,直到有一个链表为空为止。 因为链表是有序的,所以循环终止时,那个非空的链表中的元素都比后面曾经合并的链表中的元素大,所以,咱们只须要简略地将非空链表接在合并链表的前面,并返回合并链表即可。 首先咱们须要先创立一个哨兵节点,而后将prehead指向链表l1和l2中比拟小的一个。如果相等的话,指向任意一个即可。而后将较小值对应的链表的指针后移一位。 咱们上面来看一下代码实现。 def mergeTwoLists(self, l1, l2): #合并后链表的哨兵结点 head=ListNode(-1,None) pre=head #循环遍历,将两个链表中的较小值插入到合并后的链表中 while l1 and l2: if l1.val <= l2.val: pre.next=l1 l1=l1.next else: pre.next=l2 l2=l2.next pre=pre.next #将残余的非空链表插入到合并链表的前面 if l1: pre.next=l1 else: pre.next=l2 return head.next其实,咱们这里也能够应用递归的形式来实现。 class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = nextclass Solution: def mergeTwoLists(self, l1, l2): #链表l1为空,不须要合并,间接返回l2 if(l1==None): return l2 #同理,l2为空,间接返回l1即可 if(l2==None): return l1 if(l1.val<=l2.val): l1.next=self.mergeTwoLists(l1.next,l2) return l1 else: l2.next=self.mergeTwoLists(l1,l2.next) return l2问题降级LeetCode23. 合并K个升序链表 ...

November 8, 2021 · 1 min · jiezi

关于链表:完虐算法反转链表

反转链表LeetCode206. 反转链表 问题形容给定单链表的头节点 head ,请反转链表,并返回反转后的链表的头节点。示例:输出:head = [1,2,3,4,5]输入:[5,4,3,2,1]剖析问题首先,咱们依照题目的要求,先把图画进去,而后再剖析。 从图中咱们能够看到,反转前和反转后指针的指向产生了反转。所以,咱们在实现的过程中,咱们能够通过调整链表的指针来达到反转链表的目标。 咱们定义两个指针pre和cur。pre示意已反转局部的头结点,cur示意还没有反转局部的头结点。开始时cur=head,pre=None每次让cur->next=pre,实现一次部分反转。部分反转实现后,cur和pre同时向前挪动一位。循环上述过程,直到链表反转实现。 代码实现class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = nextclass Solution: def reverse(self, head): cur = head #初始化时,pre为None pre = None while cur: next=cur.next cur.next = pre pre = cur cur = next return prehead=ListNode(1,None)cur=headfor i in range(2,6): tmp=ListNode(i,None) cur.next=tmp cur=cur.nexts=Solution()pre=s.reverse(head)while pre!=None: print(pre.val) pre=pre.next更多算法题解,关注公众号《程序员学长》,欢送退出。

November 8, 2021 · 1 min · jiezi

关于链表:面试高频算法题之链表

链表全文概览 如果须要本文的PDF文档,请分割作者。或者关注公众【程序员学长】获取。 链表基础知识链表的分类链表是一种通过指针串联在一起的线性构造,次要分为单链表、双向链表和循环链表。 单链表单链表中每一个节点是由两局部组成,一个是数据域、一个是指针域(寄存指向下一个节点的指针),最初一个节点的指针域为空。 双向链表双向链表中的每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点。双向链表既能够向前查问也能够向后查问。 单向循环链表单向循环链表是指首尾相连的单链表。 双向循环链表双向循环链表是指首尾相连的双向链表。 链表的定义咱们在刷LeetCode的时候,因为链表的节点都默认定义好了,间接用就行了,然而在面试的时候,一旦要手写链表代码时,节点的定义是大家容易犯错的中央,所有咱们来看一下定义链表节点的形式。 #单链表class ListNode(object): def __init__(self, x): #数据域 self.val = x #指针域 self.next = None链表的操作单链表的删除操作删除链表中的q节点,只须要执行p->next=q->next,即让p的指针域指向q的下一个节点。 向单链表中减少一个节点在节点p后减少一个节点q,只须要执行q->next=p->next,p->next=q即可,这里肯定要留神执行的先后顺序。如果先执行p->next=q,那么原链表的p->next的信息就失落了。 解题有妙招引入哑结点哑结点也叫做哨兵节点,对于链表相干的题目,为了不便解决边界条件,个别咱们都会引入哑结点来不便求解。首先咱们来看一下什么是哑结点,哑结点是指数据域为空,指针域指向链表头节点的节点,它是为了简化边界条件而引入的。上面咱们来看一个具体的例子,例如要删除链表中的某个节点操作。常见的删除链表的操作是找到要删元素的前一个元素,如果咱们记为 pre。咱们通过:pre->next=pre->next->next来执行删除操作。如果此时要删除的是链表的第一个结点呢?就不能执行这个操作了,因为链表的第一个结点的前一个节点不存在,为空。如果此时你设置了哑结点,那么第一个结点的前一个节点就是这个哑结点。这样如果你要删除链表中的任何一个节点,都能够通过pre->next=pre->next->next的形式来进行,这就简化的代码的逻辑。 双指针法在求解链表相干的题目时,双指针也是十分罕用的思维,例如对于求解链表有环问题时,咱们申请两个指针,以一快一慢的速度在链表上行走,等他们相遇时,就能够晓得链表是否有环;或者在求链表的倒数k个节点时,申请两个指针,一个指针先走k步,而后两个指针再同时向后挪动,当先走的那个指针走到链表的开端时,另一个指针恰好指向了倒数第k个节点。 反转链表LeetCode206. 反转链表 问题形容给定单链表的头节点 head ,请反转链表,并返回反转后的链表的头节点。示例:输出:head = [1,2,3,4,5]输入:[5,4,3,2,1]剖析问题首先,咱们依照题目的要求,先把图画进去,而后再剖析。 从图中咱们能够看到,反转前和反转后指针的指向产生了反转。所以,咱们在实现的过程中,咱们能够通过调整链表的指针来达到反转链表的目标。 咱们定义两个指针pre和cur。pre示意已反转局部的头结点,cur示意还没有反转局部的头结点。开始时cur=head,pre=None每次让cur->next=pre,实现一次部分反转。部分反转实现后,cur和pre同时向前挪动一位。循环上述过程,直到链表反转实现。 代码实现class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = nextclass Solution: def reverse(self, head): cur = head #初始化时,pre为None pre = None while cur: next=cur.next cur.next = pre pre = cur cur = next return prehead=ListNode(1,None)cur=headfor i in range(2,6): tmp=ListNode(i,None) cur.next=tmp cur=cur.nexts=Solution()pre=s.reverse(head)while pre!=None: print(pre.val) pre=pre.next合并两个有序链表LeetCode21. 合并两个有序链表 ...

November 8, 2021 · 9 min · jiezi

关于链表:线性表顺序表和链表你还分不清

摘要:其实说实话,可能很多人仍然分不清线性表,程序表,和链表之间的区别和分割!本文分享自华为云社区《程序员必会本人设计线性表(程序表、链表)》,原文作者:bigsai。 前言其实说实话,可能很多人仍然分不清线性表,程序表,和链表之间的区别和分割! 线性表:逻辑构造, 就是对外裸露数据之间的关系,不关怀底层如何实现,数据结构的逻辑构造大分类就是线性构造和非线性构造而程序表、链表都是一种线性表。程序表、链表:物理构造,他是实现一个构造理论物理地址上的构造。比方程序表就是用数组实现。而链表用指针实现次要工作。不同的构造在不同的场景有不同的区别。在Java中,大家都晓得List接口类型,这就是逻辑构造,因为他就是封装了一个线性关系的一系列办法和数据。而具体的实现其实就是跟物理构造相干的内容。比方程序表的内容存储应用数组的,而后一个get,set,add办法都要基于数组来实现,而链表是基于指针的。当咱们思考对象中的数据关系就要思考指针的属性。指针的指向和value。 上面用一个图来浅析线性表的关系。可能有些不太确切,然而其中能够参考,并且前面也会依据这个图举例。 线性表根本架构对于一个线性表来说。不论它的具体实现如何,然而它们的办法函数名和实现成果应该统一(即应用办法雷同、达成逻辑上成果雷同,差异的是运行效率)。线性表的概念与Java的接口/抽象类有那么几分类似。最驰名的就是List的Arraylist和LinkedList,List是一种逻辑上的构造,示意这种构造为线性表,而ArrayList,LinkedList更多的是一种物理构造(数组和链表)。 所以基于面向对象的编程思维,咱们能够将线性表写成一个接口,而具体实现的程序表和链表的类能够实现这个线性表的办法,进步程序的可读性,还有一点比拟重要的,记得初学数据结构与算法时候实现的线性表都是固定类型(int),随着常识的提高,咱们该当采纳泛型来实现更正当。至于接口的具体设计如下: package LinerList;public interface ListInterface<T> { void Init(int initsize);//初始化表 int length(); boolean isEmpty();//是否为空 int ElemIndex(T t);//找到编号 T getElem(int index) throws Exception;//依据index获取数据 void add(int index,T t) throws Exception;//依据index插入数据 void delete(int index) throws Exception; void add(T t) throws Exception;//尾部插入 void set(int index,T t) throws Exception; String toString();//转成String输入 }程序表程序表是基于数组实现的,所以所有实现须要基于数组个性。对于程序表的构造应该有一个存储数据的数组data和无效应用长度length. 还有须要留神的是初始化数组的大小,你能够固定大小,然而笔者为了可用性如果内存不够将扩充二倍。 上面着重解说一些初学者容易混同的概念和办法实现。这里把程序表比作一队坐在板凳上的人。 插入操作add(int index,T t)其中index为插入的编号地位,t为插入的数据,插入的流程为: (1)从后(最初一个有数据位)向前到index顺次后移一位,腾出index地位的空间 (2)将待插入数据赋值到index地位上,实现插入操作 能够看得出如果程序表很长,在靠前的中央如果插入效率比拟低(插入工夫复杂度为O(n)),如果频繁的插入那么复杂度挺高的。 删除操作同理,删除也是十分占用资源的。原理和插入相似,删除index地位的操作就是从index+1开始向后顺次将数据赋值到后面地位上,具体能够看这张图: 代码实现这里我实现一个程序表给大家作为参考学习: package LinerList;public class seqlist<T> implements ListInterface<T> { private Object[] date;//数组存放数据 private int lenth; public seqlist() {//初始大小默认为10 Init(10); } public void Init(int initsize) {//初始化 this.date=new Object[initsize]; lenth=0; } public int length() { return this.lenth; } public boolean isEmpty() {//是否为空 if(this.lenth==0) return true; return false; } /* * * @param t * 返回相等后果,为-1为false */ public int ElemIndex(T t) { // TODO Auto-generated method stub for(int i=0;i<date.length;i++) { if(date[i].equals(t)) { return i; } } return -1; } /* *取得第几个元素 */ public T getElem(int index) throws Exception { // TODO Auto-generated method stub if(index<0||index>lenth-1) throw new Exception("数值越界"); return (T) date[index]; } public void add(T t) throws Exception {//尾部插入 add(lenth,t); } /* *依据编号插入 */ public void add(int index, T t) throws Exception { if(index<0||index>lenth) throw new Exception("数值越界"); if (lenth==date.length)//扩容 { Object newdate[]= new Object[lenth*2]; for(int i=0;i<lenth;i++) { newdate[i]=date[i]; } date=newdate; } for(int i=lenth-1;i>=index;i--)//前面元素后挪动 { date[i+1]=date[i]; } date[index]=t;//插入元素 lenth++;//程序表长度+1 } public void delete(int index) throws Exception { if(index<0||index>lenth-1) throw new Exception("数值越界"); for(int i=index;i<lenth;i++)//index之后元素前挪动 { date[i]=date[i+1]; } lenth--;//长度-1 } @Override public void set(int index, T t) throws Exception { if(index<0||index>lenth-1) throw new Exception("数值越界"); date[index]=t; } public String toString() { String vaString=""; for(int i=0;i<lenth;i++) { vaString+=date[i].toString()+" "; } return vaString; }}链表学习c/c++的时候链表应该是很多人感觉很绕的货色,这个很大起因可能因为指针,Java尽管不间接应用指针然而咱们也要了解指针的原理和使用。链表不同于程序表(数组)它的构造像一条链一样链接成一个线性构造,而链表中每一个节点都存在不同的地址中,链表你能够了解为它存储了指向节点(区域)的地址,可能通过这个指针找到对应节点。 ...

June 29, 2021 · 3 min · jiezi

关于链表:java实现单向链表

单向链表以节点的形式来存储,蕴含data和next在内存中,链表的各个节点不肯定是间断存储的链表分有头节点的链表和没有头节点的链表,头节点用head示意代码次要是单向链表的增删改查import java.util.Stack;public class Demo2 { public static void main(String[] args) { MyLinkedList linkedList = new MyLinkedList(); //创立用户节点,并插入链表 UserNode user1 = new UserNode(1, "一一"); UserNode user3 = new UserNode(3, "三三"); UserNode user2 = new UserNode(2, "二二"); linkedList.addNode(user1); linkedList.addNode(user3); linkedList.addNode(user2); linkedList.getList(); System.out.println(); //按id大小插入 System.out.println("有序插入"); UserNode user5 = new UserNode(5, "五五"); UserNode user4 = new UserNode(4, "四四"); linkedList.addByOrder(user5); linkedList.addByOrder(user4); linkedList.getList(); System.out.println(); //按id批改用户信息 System.out.println("批改用户信息"); UserNode userEdit = new UserNode(1, "新一一"); linkedList.changeNode(userEdit); linkedList.getList(); System.out.println(); //依据id删除用户信息 System.out.println("删除用户信息"); linkedList.deleteNode(new UserNode(3, "")); linkedList.getList(); System.out.println(); //取得倒数第几个节点 System.out.println("取得倒数节点"); System.out.println(linkedList.getUserByRec(2)); System.out.println(); //翻转链表 System.out.println("翻转链表"); MyLinkedList newLinkedList = linkedList.reverseList(); newLinkedList.getList(); System.out.println(); //顺叙遍历链表 System.out.println("倒序遍历链表"); newLinkedList.reverseTraverse(); }}/** * 创立链表 */class MyLinkedList { private UserNode head = new UserNode(0, ""); /** * 在链表尾部增加节点 * * @param node 要增加的节点 */ public void addNode(UserNode node) { //创立一个辅助节点,用于遍历 UserNode temp = head; //找到最初一个节点 while (true) { //temp是尾节点就进行循环 if (temp.next == null) { break; } //不是尾结点就向后挪动 temp = temp.next; } temp.next = node; } /** * 遍历链表 */ public void getList() { System.out.println("开始遍历链表"); if (head.next == null) { System.out.println("链表为空"); } //创立辅助节点 UserNode temp = head.next; while (true) { //遍历实现就进行循环 if (temp == null) { break; } System.out.println(temp); temp = temp.next; } } /** * 按id程序插入节点 * * @param node */ public void addByOrder(UserNode node) { //如果一个节点都没用,直接插入 if (head.next == null) { head.next = node; return; } UserNode temp = head; while (temp.next != null && temp.next.id < node.id) { temp = temp.next; } //当指标node的id最大时,则不会执行if中的语句 if (temp.next != null) { node.next = temp.next; } temp.next = node; } /** * 依据id来批改节点信息 * * @param node 批改信息的节点 */ public void changeNode(UserNode node) { UserNode temp = head; //遍历链表,找到要批改的节点 while (temp.next != null && temp.id != node.id) { temp = temp.next; } //如果temp曾经是最初一个节点,判断id是否相等 if (temp.id != node.id) { System.out.println("未找到该用户的信息,请先创立该用户的信息"); return; } //批改用户信息 temp.name = node.name; } /** * 依据id删除节点,找到待删除节点的上一个节点 * * @param node 要删除的节点 */ public void deleteNode(UserNode node) { if (head.next == null) { System.out.println("链表为空"); return; } UserNode temp = head; //遍历链表,找到要删除的节点 while (temp.next != null && temp.next.id != node.id) { temp = temp.next; } //判断最初一个节点的是否要删除的节点 if (temp.next.id != node.id) { System.out.println("请先插入该用户信息"); return; } //删除该节点 temp.next = temp.next.next; } /** * 失去倒数的节点 * * @param index 倒数第几个数 * @return */ public UserNode getUserByRec(int index) { if (head.next == null) { System.out.println("链表为空!"); } UserNode temp = head.next; //记录链表长度 //所以length初始化为1 int length = 1; while (temp.next != null) { temp = temp.next; length++; } if (length < index) { throw new RuntimeException("链表越界"); } //假如有五个,倒数第一个相当于负数第五个 temp = head.next; for (int i = 0; i < length - index; i++) { temp = temp.next; } return temp; } /** * 翻转链表 * * @return 反转后的链表 */ public MyLinkedList reverseList() { //链表为空或者只有一个节点,无需翻转 if (head.next == null || head.next.next == null) { System.out.println("无需翻转"); } MyLinkedList newLinkedList = new MyLinkedList(); //用于保留正在遍历的节点 UserNode temp = head.next; //用于保留正在遍历节点的下一个节点 UserNode nextNode = temp.next; while (true) { //插入新链表 temp.next = newLinkedList.head.next; newLinkedList.head.next = temp; //挪动到下一个节点 temp = nextNode; nextNode = nextNode.next; if (temp.next == null) { //插入最初一个节点 temp.next = newLinkedList.head.next; newLinkedList.head.next = temp; head.next = null; return newLinkedList; } } } public void reverseTraverse() { if (head == null) { System.out.println("链表为空"); } UserNode temp = head.next; //创立栈,用于寄存遍历到的节点 Stack<UserNode> stack = new Stack<>(); while (temp != null) { stack.push(temp); temp = temp.next; } while (!stack.isEmpty()) { System.out.println(stack.pop()); } }}/** * 定义节点 */class UserNode { int id; String name; //用于保留下一个节点的地址 UserNode next; public UserNode(int id, String name) { this.id = id; this.name = name; } @Override public String toString() { return "UserNode{" + "id=" + id + ", name='" + name + '\'' + '}'; }}

June 9, 2021 · 3 min · jiezi

关于链表:数据结构与算法一文搞懂线性表顺序表链表

原创公众号:bigsai 文章珍藏在GitHub前言通过后面数据结构与算法前导我么晓得了数据结构的一些概念和重要性,那么咱们明天总结下线性表相干的内容。当然,我用本人的了解解分享给大家。 其实说实话,可能很多人仍然分不清线性表,程序表,和链表之间的区别和分割! 线性表:逻辑构造, 就是对外裸露数据之间的关系,不关怀底层如何实现。程序表、链表:物理构造,他是实现一个构造理论物理地址上的构造。比方程序表就是用数组实现。而链表用指针实现次要工作。不同的构造在不同的场景有不同的区别。对于java来说,大家都晓得List接口类型,这就是逻辑构造,因为他就是封装了一个线性关系的一系列办法和数据。而具体的实现其实就是跟物理构造相干的内容。比方程序表的内容存储应用数组的,而后一个get,set,add办法都要基于数组来实现,而链表是基于指针的。当咱们思考对象中的数据关系就要思考指针的属性。指针的指向和value。 上面用一个图来浅析线性表的关系。可能有些不太确切,然而其中能够参考,并且前面也会依据这个图举例。 线性表根本架构对于一个线性表来说。不论它的具体实现办法如何,咱们应该有的函数名称和实现成果应该统一。你也能够感觉的到在一些构造的设计。比方List的Arraylist和LinkedList。Map的HashMap和currentHashMap他们的接口api都是雷同的,然而底层设计实现必定是有区别的。 所以,基于面向对象的编程思维,咱们能够将线性表写成一个接口,而具体实现的程序表和链表能够继承这个接口的办法,进步程序的可读性。 还有一点比拟重要的,记得初学数据结构与算法时候实现的线性表都是固定类型(int),随着常识的提高,咱们该当采纳泛型来实现更正当。至于接口的具体设计如下: package LinerList;public interface ListInterface<T> { void Init(int initsize);//初始化表 int length(); boolean isEmpty();//是否为空 int ElemIndex(T t);//找到编号 T getElem(int index) throws Exception;//依据index获取数据 void add(int index,T t) throws Exception;//依据index插入数据 void delete(int index) throws Exception; void add(T t) throws Exception;//尾部插入 void set(int index,T t) throws Exception; String toString();//转成String输入 }程序表程序表是基于数组实现的,所以一些办法要基于数组的个性。对于程序表应该有的根底属性为一个数组data和一个length. 还有须要留神的是初始化数组的大小,你能够固定大小,然而笔者为了可用性如果内存不够将扩充二倍。当然这样很可能因为空间应用问题造成很大的节约。 一些根本的额办法就不说了,上面着重解说一些初学者容易混同的概念和办法实现。这里把程序表比作一队坐在板凳上的人。 插入add(int index,T t) 其中index为插入的编号地位,t为插入的数据-依据图片你就很好了解插入操作。当插入一个index时候,他的前面所有元素都要后移一位。你能够看的出插入时候整个操作的臃肿性。所以这也是程序表性能体现最差的中央,频繁的插入,删除。 删除同理,删除也是十分占用资源的。原理和插入相似,不过人走了,空一个小板凳前面的人须要往前挪。 其余操作其余操作就很简略了。比方如果依照编号获取数据getElem(int index),你能够间接依据数据坐标返回。a[index],而其余操作,能够通过遍历间接操作数组即可。 链表我想,表应该是很多人感觉很绕的货色,这个很大起因可能因为指针。很多人说java没指针,其实java他也有隐形指针。只不过不能间接用罢了。 指针建设的数据关系往往比数组这些要形象的多。对于指针域,你把他当成一个对象就好了,不过这个对象指向的是另一个同等级对象。对于这个关系,你能够比作每个person类。每个person类都有老公(老婆),而这个老公老婆也是一个理论对象,能够了解这更像一种逻辑约定关系,而不是硬生生的关系吧。 指针你能够思考成脑子记忆。下面的程序表咱们说它有序因为每个小板凳(数组)有编号,咱们能够依据这个来确定地位。而对于链表来说,你能够看作成一个站在操场上的一队人。而他的操作也略有不同,上面针对一些比拟非凡和重要的进行演绎。 ...

December 3, 2020 · 3 min · jiezi

关于链表:链表

链表简介数组:对于索引有语义的状况,利用索引获取值,疾速查问。 随机拜访的能力链表:不适宜用于索引有语义的状况 真正的动静,不须要解决固定容量的问题构建构建节点类<?phpdeclare(strict_types=1);class Node{ // 该节点的元素值 public $e; // 下一个节点的指针 public $next; public function __construct($e = null, $next = null) { $this->e = $e; $this->next = $next; } // 将该节点对象的元素值用字符串输入 public function __toString(): string { return (string)$this->e; }} 构建链表实现类定义属性与构造函数<?phpdeclare(strict_types=1);require_once 'Node.php';class LinkedList{ // 链表头指针 private $head; // 链表中元素数量 private int $size; public function __construct() { $this->head = null; $this->size = 0; }} 获取链表无效元素数量// 获取链表无效元素数量public function getSize(): int{ return $this->size;} ...

November 8, 2020 · 6 min · jiezi

关于链表:C-单链表头插法尾插法删除指定值结点

先上代码#include <stdio.h>#include <stdlib.h>typedef struct nodeList { int data; //数据域 struct nodeList *next; //指针域} nodeList;//遍历链表void TraverseList(struct nodeList *head){ while(head != NULL){ printf("%d \n",head->data); head=head->next; }}//头插法nodeList *HeadCreatList(struct nodeList *head, int n){ struct nodeList *node; for (int i = 0; i < n; ++i) { node=(struct nodeList *)malloc(sizeof(struct nodeList)); node->data = i; node->next = head->next; head->next = node; } head->data = n; return head;}//尾插法nodeList *TailCreatList(struct nodeList *head, int n){ struct nodeList *node,*end; end = head; for (int i = 0; i < n; ++i) { node=(struct nodeList *)malloc(sizeof(struct nodeList)); node->data = i; end->next = node; end = node; } head->data = n; return head;}//删除某个值nodeList *deleteFromList(struct nodeList *head, int target){ struct nodeList *pre,*tmp; int count=0; tmp = head; pre = head; head= head->next; while(head != NULL){ if (head->data == target){ pre->next = head->next; count--; }else{ pre = head; count++; } head= head->next; } tmp->data = count; return tmp;}int main(){ struct nodeList *head,*node; head=(struct nodeList *)malloc(sizeof(struct nodeList)); //头结点 //head->data = NULL; head->next = NULL; //头插法 head = HeadCreatList(head, 5); printf("头插法: \n"); TraverseList(head); //尾插法 head = TailCreatList(head, 5); printf("尾插法: \n"); TraverseList(head); //删除 head = deleteFromList(head,2); printf("删除2: \n"); TraverseList(head); return 0;}执行后果: ...

October 26, 2020 · 1 min · jiezi

关于链表:PHP-和-Go-实现环路链表检测

原文链接:何晓东 博客 环路链表检测给定一个链表,如果它是有环链表,实现一个算法返回环路的结尾节点。有环链表的定义:在链表中某个节点的next元素指向在它后面呈现过的节点,则表明该链表存在环路。 起源:力扣(LeetCode)链接:https://leetcode-cn.com/probl... 解题思路 1遍历链表,同时将每次的后果放到 map 中,如果有元素反复呈现,则是有环形链表 代码/** * Definition for singly-linked list. * type ListNode struct { * Val int * Next *ListNode * } */func detectCycle(head *ListNode) *ListNode { visited := make(map[*ListNode]struct{}, 1) work := head for work != nil { _, ok := visited[work] if ok { return work } else { visited[work] = struct{}{} } work = work.Next } return nil}解题思路 2快慢指针求解:咱们定义两个指针,一快一满。慢指针每次只挪动一步,而快指针每次挪动两步。初始时,慢指针在地位 head,而快指针在地位 head.next。这样一来,如果在挪动的过程中,快指针反过来追上慢指针,就阐明该链表为环形链表。否则快指针将达到链表尾部,该链表不为环形链表。 ...

October 9, 2020 · 1 min · jiezi

关于链表:结构与算法03单向链表和双向链表

本文源码:GitHub·点这里 || GitEE·点这里 一、链表简介1、链表概念链表是一种物理存储单元上非间断、非程序的存储构造,数据元素的逻辑程序是通过链表中的指针链接秩序实现的。链表由一系列节点组成,节点能够在运行时动静生成,节点包含两个局部:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 2、根底特点内存存储 逻辑构造 特点形容 物理存储上是无序且不间断的;链表是由多个节点以链式构造组成;逻辑层面上看造成一个有序的链路构造;链表构造解决数组存储须要事后晓得元素个数的缺点,能够充分利用内存空间,实现灵便的内存动静治理。 二、单向链表1、根底形容 单向链表是链表的一种,其特点是链表的链接方向是单向的,链表的遍历要从头部开始程序读取;结点形成,head指针指向第一个成为表头结点,终止于最初一个指向NULL的指针。 2、根底操作增加数据 初始化head节点,作为链表的头;批改以后开端节点的next指针;新增加的节点房子在链表开端;删除数据 遍历找到要删除的节点,把删除节点前个节点的指针指向该删除节点的下个节点; 三、双向链表1、概念形容 双向链表也叫双链表,是链表的一种,链表的每个数据结点中都有两个指针,别离指向间接后继和间接前驱,从双向链表中的任意一个结点开始,都能够很疾速地拜访它的前驱结点和后继结点,链表构造的应用少数都是结构双向循环链表。 2、根底操作增加数据 遍历找到链表的最初一个节点;批改以后开端节点的next指针;新增加的节点房子在链表开端;增加最新尾节点的prev指针;删除数据 双向链表,基于要删除节点操作即可;操作上图中要删除的Node2节点;Node2.prev.next = Node2.next;Node2.next.prev = Node2.prev;通过上述流程的操作,就把链表中一个节点删除,剩下节点再度连接成链式构造。 3、源码剖析在Java的API中,LinkedList是典型的双向链表构造,上面基于LinkedList源码看双向链表的操作。 根底案例 public class M01_Linked { public static void main(String[] args) { List<User> userList = new LinkedList<>() ; User removeUser = new User(200,"Second") ; // 增加元素 userList.add(new User(100,"First")) ; userList.add(removeUser) ; userList.add(new User(300,"Third")) ; System.out.println("初始化:"+userList); // 批改元素 userList.get(0).setUserName("Zero"); System.out.println("批改后:"+userList); // 删除元素 userList.remove(removeUser) ; System.out.println("删除后:"+userList); }}class User { private Integer userId ; private String userName ; public User(Integer userId, String userName) { this.userId = userId; this.userName = userName; } @Override public String toString() { return "User{" + "userId=" + userId + ", userName='" + userName + '\'' + '}'; } // 省略Get和Set办法}节点形容 ...

September 18, 2020 · 2 min · jiezi

关于链表:一周刷完剑指offer16合并两个有序的链表

合并两个有序的链表1. 题目形容输出两个枯燥递增的链表,输入两个链表合成后的链表,当然咱们须要合成后的链表满足枯燥不减规定。 2. 示例无 3. 解题思路思路:非递归比拟两个链表的首结点,哪个小的的结点则合并到第三个链表尾结点,并向前挪动一个结点。 步骤一后果会有一个链表先遍历完结,或者没有 第三个链表尾结点指向残余未遍历完结的链表 返回第三个链表首结点 递归办法: 4. Java实现非递归的实现办法:应用一个辅助的头部节点: ListNode root = new ListNode(-1)/*public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; }}*/public class Solution { public ListNode Merge(ListNode list1,ListNode list2) { if (list1 == null){ return list2; } if (list2 == null){ return list1; } ListNode node = new ListNode(-1); // 用于保留头部节点 ListNode root = node; while (list1 != null && list2 != null){ if (list1.val < list2.val){ node.next = list1; node = node.next; // 更新node节点,指向下一个 list1 = list1.next; }else{ node.next = list2; node = node.next; list2 = list2.next; } } // 可能某个链表还残余值,下列例子就链表2 会残余 // 比方链表1: 1->2->4 // 链表2: 3->5->6 if(list1 == null){ node.next = list2; } if (list2 == null){ node.next = list1; } return root.next; }}应用递归的实现办法:/*public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; }}*/public class Solution { public ListNode Merge(ListNode list1,ListNode list2) { if (list1 == null){ return list2; } if (list2 == null){ return list1; } ListNode ret = null; if (list1.val < list2.val){ ret = list1; ret.next = Merge(list1.next, list2); }else{ ret = list2; ret.next = Merge(list1, list2.next); } return ret; }}5. Python实现比较简单的非递归的实现办法:#非递归实现合并两个有序的链表class Solution1: # 返回合并后列表 def Merge(self, pHead1, pHead2): if not pHead1: return pHead2 if not pHead2: return pHead1 start = None p = None while pHead1 and pHead2: if pHead1.val <= pHead2.val: print(pHead1.val) if start is None: start = p = pHead1 else: p.next = pHead1 p = p.next #更新p 结点 pHead1 = pHead1.next # 更新有序链表的结点 else: if start is None: start = p = pHead2 else: p.next = pHead2 p = p.next pHead2 = pHead2.next #可能某个链表始终还剩值 if not pHead1: #如果第一个链表都是空的话 p.next = pHead2 else: p.next = pHead1 return start 应用递归的实现办法:# -*- coding:utf-8 -*-# class ListNode:# def __init__(self, x):# self.val = x# self.next = Noneclass Solution: # 返回合并后列表 def Merge(self, pHead1, pHead2): # write code here if not pHead1: #如果一个链表不存在的话,返回另外一个链表 return pHead2 if not pHead2: return pHead1 if pHead1.val <= pHead2.val: ret = pHead1 ret.next = self.Merge(pHead1.next, pHead2) else: ret = pHead2 ret.next = self.Merge(pHead1, pHead2.next) return ret 如果您感觉本文有用,请点个“在看” ...

September 17, 2020 · 2 min · jiezi

关于链表:一周刷完剑指offer15反转单链表

反转单链表1. 题目形容输出一个链表,反转链表后,输入新链表的表头。 2. 示例无 3. 解题思路输出的链表头指针为None或者整个链表只有一个结点时,反转后的链表呈现断裂,返回的翻转之后的头节点不是原始链表的尾结点。因而须要引入一个翻转后的头结点,以及一个指向以后结点的指针,一个指向以后结点前一个结点的指针,一个指向以后结点后一个结点的指针,防止出现断裂。 递归反转链表实现,绝对比拟容易一些 4. Java实现比较简单的非递归的实现办法:/*public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; }}*/public class Solution { public ListNode ReverseList(ListNode head) { // 如果是head为空,或者只有一个节点,则间接返回 if (head == null || head.next == null){ return head; } ListNode p = head; ListNode newHead = null; // 新的头节点 while(p != null){ ListNode temp = p.next; // 保留p指向下一个节点的指针 p.next = newHead; newHead = p; // 更新头节点 p = temp; // 即 p = p.next ,更新p节点 } return newHead; }}应用递归的实现办法:/*public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; }}*/public class Solution { public ListNode ReverseList(ListNode head) { // 应用递归的办法 if (head == null || head.next == null){ return head; } ListNode headNext = ReverseList(head.next); head.next.next = head; head.next = null; return headNext; }}5. Python实现比较简单的非递归的实现办法:#!/usr/bin/env python# -*- coding: utf-8 -*-"""__title__ = ''__author__ = 'mike_jun'__mtime__ = '2019-5-24'#目标: 反转单链表"""def reverseLink(head): #pass if not head or not head.next: return head p = head newHead = None while p: temp = p.next p.next = newHead newHead = p p = temp return newHead #创立一个单链表class Node(): def __init__(self, values): self.next = None self.values = values node1 = Node(1)node2 = Node(2)node3 = Node(3)node4 = Node(4)node1.next = node2node2.next = node3node3.next = node4 def printNode(head): result = [] while head: result.append(head.values) head = head.next print(result)printNode(node1)newNode = reverseLink(node1)printNode(newNode)应用递归的实现办法:/*pythonpublic class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; }}*/public class Solution { public ListNode ReverseList(ListNode head) { // 应用递归的办法 if (head == null || head.next == null){ return head; } ListNode headNext = ReverseList(head.next); head.next.next = head; head.next = null; return headNext; }}如果您感觉本文有用,请点个“在看” ...

September 17, 2020 · 2 min · jiezi

关于链表:一周刷完剑指offer14链表中倒数第k个结点

链表中倒数第k个结点1. 题目形容输出一个链表,输入该链表中倒数第k个结点。 2. 示例无 3. 解题思路应用双指针的办法: 如果在只心愿一次遍历的状况下, 寻找倒数第k个结点, 能够设置两个指针 第一个指针先往前走k-1步, 而后从第k步开始第二个指针指向头结点 而后两个指针一起遍历 当地一个指针指向尾节点的时候, 第二个指针正好指向倒数第k个结点 4. Java实现/*public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; }}*/public class Solution { public ListNode FindKthToTail(ListNode head,int k) { // 快指针先走 k-1 步,慢指针指向头节点,两个节点一起走 // 当快指针走到尾,则慢指针找到 倒数 第 k 个节点 if (k <= 0 || head == null){ return null; } ListNode fastNode = head; ListNode slow = head; for (int i=0; i<k-1; i++){ if (fastNode.next == null){ // 判断是否会越界 return null; } fastNode = fastNode.next; } while(fastNode.next != null){ slow = slow.next; fastNode = fastNode.next; } return slow; }}5. Python实现# -*- coding:utf-8 -*-# class ListNode:# def __init__(self, x):# self.val = x# self.next = None class Solution: def FindKthToTail(self, head, k): # write code here if not head or k <= 0: return None pBehind = head for i in range(k-1): #找到第 k-1 个结点 if pBehind.next != None: pBehind = pBehind.next else: return None pHead = head while pBehind.next != None: # 而后一起往前走 pBehind = pBehind.next pHead = pHead.next return pHead 如果您感觉本文有用,请点个“在看” ...

September 17, 2020 · 1 min · jiezi