链表

golang中container/ring包用法

自古美人都是妖i 提交于 2020-02-29 06:29:18
ring包实现了环形链表的操作。 type Ring //Ring类型代表环形链表的一个元素,同时也代表链表本身。环形链表没有头尾;指向环形链表任一元素的指针都可以作为整个环形链表看待。Ring零值是具有一个(Value字段为nil的)元素的链表。 type Ring struct { Value interface{} // 供调用者使用,本包不会对该值进行操作 // 包含未导出字段 } func New(n int) *Ring //创建一个长度为n的环形链表 func (r *Ring) Do(f func(interface{})) //对链表中任意元素执行f操作,如果f改变了r,则该操作造成的后果是不可预期的。 func (r *Ring) Len() int //求环长度,返回环中元素数量 func (r *Ring) Link(s *Ring) *Ring //Link连接r和s,并返回r原本的后继元素r.Next()。r不能为空。 如果r和s指向同一个环形链表,则会删除掉r和s之间的元素,删掉的元素构成一个子链表,返回指向该子链表的指针(r的原后继元素);如果没有删除元素,则仍然返回r的原后继元素,而不是nil。如果r和s指向不同的链表,将创建一个单独的链表,将s指向的链表插入r后面,返回s原最后一个元素后面的元素(即r的原后继元素)。 func (r *Ring)

数据结构与算法分析:(五)循环链表

自闭症网瘾萝莉.ら 提交于 2020-02-29 02:05:50
一、前言 相信小伙伴们在前面两篇文章的详细介绍已经对单向链表、双向链表有一个很清晰的认识了。 数据结构与算法分析:(三)单向链表 数据结构与算法分析:(四)双向链表 接下来我们来介绍循环链表,你把单向链表、双向链表搞清楚了的话,循环链表自然也不难了。 循环链表可分为:单向循环链表、双向循环链表。 1、单向循环链表: 单向循环链表跟单向链表唯一的区别就在 尾结点 。我们知道,单向链表的尾结点指针指向空地址,表示这就是最后的结点了。而单向循环链表的尾结点指针是指向链表的头结点。从我画的单向循环链表图中,你应该可以看出来,它像一个环一样首尾相连,所以叫作“单向循环”链表。 和单向链表相比,单向循环链表的优点是从链尾到链头比较方便。当要处理的数据具有环型结构特点时,就特别适合采用单向循环链表。比如著名的 约瑟夫问题 。尽管用单向链表也可以实现,但是用单向循环链表实现的话,代码就会简洁很多。 2、双向循环链表: 双向循环链表想必也不用我多说了吧。 二、循环链表实战 假设有这么一个小游戏: 100个人围成圆圈,从1开始报数,喊到3人的时候退出,重复,直到剩下最后一个人。 看到围成圆圈,立马想到了 循环链表 这个数据结构。 1、我们先来定义循环链表的接口 public interface CircularLinkedList < E > { /** * 向链表插入一个元素,默认在尾部 *

线索化二叉树的概念以及前序线索化

最后都变了- 提交于 2020-02-29 01:45:37
** 二叉树和线索化二叉树 ** 以此谨记自己学习java心得 什么是二叉树,用我自己的话说。一般简单数据结构是数组和链表,昨天学了哈希表,是一个数组加上一个单向链表完成的。但是哈希表在查找某单元时,需要从头找到尾。而二叉树可以根据大小来分配,像链表里的结构一般是有previous 和next。而二叉树则是left和right。又因为考虑到完全二叉树的最后一排以及倒数第二排的一些元素,它们的指针是空的,也就是浪费了left和right内存空间,线索二叉树则将这些没有指向具体的内存的left和right给用了起来。 如图中8,10,14的left和right没有充分利用,6的right没有充分利用,在线索化二叉树中,根据前序、中序、后序可将这些left,right分别指向排列时元素的前驱和后继。 比方说,若按前序排序(可理解为 先中再左再有右):1,3,8,10,6,14。在该排序中则将8的left 指向3,8的right指向10。6的right指向14.依次类推。中序和后序的话,也是按照排序的先后,将left指向前驱,将right指向后继。 注:若某结点原本就有指向其他地址的left或者right,则这些有指向left或right原封不动。 在这里张贴一下自己的前序排序线索化的代码。 public void prologue ( HeroNode node ) { if (

剑指Offer刷题总结

你。 提交于 2020-02-28 21:50:31
1. 二维数组中的查找 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。 思路题 从右上角或者左下角开始缩小范围。 2. 替换空格 请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。 语法题 3. 从尾到头打印链表 输入一个链表,按链表从尾到头的顺序返回一个ArrayList。 头插法 数组反转 栈 递归 4. 重建二叉树 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。 递归 API:Arrays.copyOfRange() 5. 用两个栈实现队列 用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。 简单题 6. 旋转数组的最小数字 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5

1111111111111 算法汇总

为君一笑 提交于 2020-02-28 21:01:49
如何找到两个相交链表对交点? 如果两个单向链表有公共节点,则两个链表会构成Y型结构,最后一个节点相同 我们可以从头开始遍历两个链表,找到最后一个节点的指针,设为p_a,p_b。同时记录下两个链表的长度len_a,len_b(假设len_a >= len_b)。 如果p_a == p_b,则说明两个链表有公共节点,否则没有。 如果有公共节点,则第一个公共节点距起始节点的距离满足 len_a - start_a == len_b - start_b。 所以第一个可能的公共节点距起始节点的距离是 len_a - len_b, 0。我们从这两个节点开始比较,直到找到第一个公共节点。 单向链表中,如何在给定节点前快速插入一个节点? 对于单向链表来说,在某个节点后面插入一个新节点是非常快的。 所以我们可以在给定节点后面插入一个新节点,然后交换给定节点和新节点的数据即可。 找到链表的倒数第m个节点 方法1: 首先遍历链表,统计链表的长度N。 然后再次遍历链表,找到第N-m+1个节点,即为倒数第m个节点。 方法2: 使用两个指针,并使它们指向的节点相距m-1个。 然后同时向前移动两个指针,当一个指针指最后一个节点时,第二个指针指向倒数第m个节点。 两个方法的复杂度都是O(n)。 但是当N较大而m较小时,方法2可能会更快一些。因为方法2能更好利用CPU的缓存。 更多阅读: http://baike

LeetCode 8. 合并两个有序链表

可紊 提交于 2020-02-28 20:21:46
题目描述 难度:简单 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例: 输入:1->2->4, 1->3->4 输出:1->1->2->3->4->4 解题思路 对于两个有序链表 l1:1->5->8 和 l2:2->3->9, 首先将l4=l3; 利用一个while循环,遍历l1和l2, val比较小的ListNode,则将其赋予给L3.next;直到l1和l2都遍历完成; 具体过程如下: 所以l4一开始指向的内存地址是一直不变的,不管l3如何变,l4还是指向一开始指向的位置,所以当l3不断往下走, 就不断往这段内存里面添加新的对象,l3一直只有一个结点,而l4指向了这段内存的头部,所以拥有多个结点 这样看来,只使用一条链表来遍历是行不通的,可能到最后只输出一个结点,用两个链表就可以解决这一问题了。 代码如下 public class MergeTwoLists { public ListNode mergeTwoLists(ListNode l1, ListNode l2) { if (l1==null&&l2==null) { return null; }else if (l1==null) { return l2; }else if (l2==null) { return l1; } ListNode l3=new

《Linux内核分析》MOOC课程之从迷你Linux内核角度理解进程时间轮片调度(未完)

北城余情 提交于 2020-02-28 19:53:38
代码分析 mypcb.h mymain.c 上面这段代码主要完成了对0号进程的初始化,即pid置为0,状态state置为0(即runnable状态),进程入口及当前进程的线程的ip指向my_process,线程的sp指向当前进程的进程堆栈,由于目前只有0号进程,所以next指针指向自己形成一个单PCB链表。 上面这段代码主要是扩充循环链表,使用memcpy()复制0号进程的状态给创建的从1号到MAX_TASK_NUM-1号进程,并与0号进程一起构成一个循环PCB链表。 上面这段代码功能是从循环PCB链表的task[0]启动0号进程,是通过使用了gcc的内联汇编来实现的。接下来我们具体分析该过程: 初始堆栈状态: movl %1, %%esp push1 %1 pushl %0 ret popl %%ebp 其实这段汇编代码是不会被执行的,因为ret \n\t后eip指向了0号进程的起始地址。 上面这段代码实现每循环1千万次打印一下当前进程的pid,然后判断时钟中断是否将调度标志(my_need_sched)置为1,如果是1则将调度标志置为0,调用my_schedule(),避免消息机制,然后再打印一次当前进程的pid。 myinterrupt.c my_time_handler()实现被调用每千次且调度标志(my_need_sched)不为1时,打印“>>>my_timer

Java 双向链表的增删改查

放肆的年华 提交于 2020-02-28 18:57:53
双向链表 简介 双向链表是基于单链表的基础上,每个节点中添加了一个指向上一个节点的指针。相对单链表而言,双向链表中的增删除改更为方便。例如,双链表可以便捷地进行自我删除的(单链表则需要借助辅助节点),届时只需要需要删除节点的相邻节点进行链接,便可实现自我删除。 功能实现 定义双链表节点 在单链表的基础上,添加一个存储上一个节点的指针。 class Node { int no; String name; Node next; Node pre; /** * 构造函数 */ public Node(int no, String data) { this.no = no; this.name = data; } } 遍历 双链表的遍历也是和单链表一样的。只需要从头结点开始,逐个遍历便可。判断题条件是当前节点的下一个节点不为空(temp.next != null)。 完整代码: public void printAll() { if (headNode.next == null) { System.out.println("链表为空~!"); return; } Node temp = headNode; while (null != temp.next) { temp = temp.next; System.out.println("编号: " + temp.no + " \t姓名:"

Java数据结构总结

懵懂的女人 提交于 2020-02-28 18:25:16
一:通过一些源码展示各种数据结构的使用方法: 1.顺序表 概念及结构 : 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组 上完成数据的增删查改 public interface ISequence { //在pos位置插入val boolean add(int pos,Object data); //查找关键字key 找到返回key的下标,没有返回null; int search(Object key); //查找是否包含关键字key是否在顺序表当中(这个和search有点冲突) boolean contains(Object key); //得到pos位置的值 Object getPos(int pos); //删除第一次出现的关键字key Object remove(Object key); //得到顺序表的长度 int size(); //打印顺序表 void display(); //清空顺序表以防内存泄漏 void clear(); } 2.链表 概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的引用链 接次序实现的 。 链表的种类: 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结 构,如哈希桶、图的邻接表等等。 带头循环单链表

链表经典笔试题

ぐ巨炮叔叔 提交于 2020-02-28 18:00:55
1.将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的 public ListNode mergeTwoLists ( ListNode l1 , ListNode l2 ) { if ( l1 == null ) { //l1为空,最终结果就是l2 return l2 ; } if ( l2 == null ) { //l2为空,最终结果就是l2 return l1 ; } ListNode newHead = new ListNode ( - 1 ) ; //引入傀儡结点 ListNode newTail = newHead ; ListNode cur1 = l1 ; ListNode cur2 = l2 ; while ( cur1 != null && cur2 != null ) { if ( cur1 . val < cur2 . val ) { //把cur1对应的结点插入到新链表的末尾 //此时需要考虑两种情况,newTail为null和非null的情况 newTail . next = cur1 ; newTail = newTail . next ; cur1 = cur1 . next ; } else { newTail . next = cur2 ; newTail = newTail . next ; cur2