链表

环形链表

旧城冷巷雨未停 提交于 2020-03-23 21:46:44
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。 说明:不允许修改给定的链表。 示例 1: 输入:head = [3,2,0,-4], pos = 1 输出:tail connects to node index 1 解释:链表中有一个环,其尾部连接到第二个节点。 示例 2: 输入:head = [1,2], pos = 0 输出:tail connects to node index 0 解释:链表中有一个环,其尾部连接到第一个节点。 示例 3: 输入:head = [1], pos = -1 输出:no cycle 解释:链表中没有环。 解题思路: 构建双指针第一次相遇: 设两指针 fast,slow 指向链表头部 head,fast 每轮走 2 步,slow 每轮走 1 步; 若 fast 指针走过链表末端,说明链表无环,直接返回 null(因为每走 1轮,fast 与 slow 的间距 +1,若有环,快慢两指针终会相遇); 当 fast == slow 时,代表两指针在环中 第一次相遇,此时执行 break 跳出迭代; 第一次相遇时步数分析: 设链 表头部到环需要走 a 步 , 链表环走一圈需要 b

ConcurrentHashMap原理

不想你离开。 提交于 2020-03-23 21:30:51
转载地址:http://www.blogjava.net/xylz/archive/2010/07/20/326661.html 在上一篇中介绍了HashMap的原理,这一节是ConcurrentMap的最后一节,所以会完整的介绍ConcurrentHashMap的实现。 ConcurrentHashMap原理 在 读写锁章节部分 介绍过一种是用读写锁实现Map的方法。此种方法看起来可以实现Map响应的功能,而且吞吐量也应该不错。但是通过前面对 读写锁原理 的分析后知道,读写锁的适合场景是读操作>>写操作,也就是读操作应该占据大部分操作,另外读写锁存在一个很严重的问题是读写操作不能同时发生。要想解决读写同时进行问题(至少不同元素的读写分离),那么就只能将锁拆分,不同的元素拥有不同的锁,这种技术就是“锁分离”技术。 默认情况下ConcurrentHashMap是用了16个类似HashMap 的结构,其中每一个HashMap拥有一个独占锁。也就是说最终的效果就是通过某种Hash算法,将任何一个元素均匀的映射到某个HashMap的Map.Entry上面,而对某个一个元素的操作就集中在其分布的HashMap上,与其它HashMap无关。这样就支持最多16个并发的写操作。 上图就是ConcurrentHashMap的类图。参考上面的说明和HashMap的原理分析

二叉搜索树与双向链表

て烟熏妆下的殇ゞ 提交于 2020-03-23 17:21:25
1:题目描述 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。 为了让您更好地理解问题,以下面的二叉搜索树为例: 我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。 下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。 特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-yu-shuang-xiang-lian-biao-lcof 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 2:题目分析 解题思路:对于二叉排序树而言,其中序遍历输出就是升序排序。那我们怎么将其做成双向链表尼?着手点肯定是中序遍历;在遍历的迭代逻辑中,我们要将前次迭代和本次迭代处理的节点,按照双向链表的要求进行处理。怎么处理尼?通过一个全局变量保存上次遍历的节点,然后再本次遍历的节点处理中进行如下操作: preNode.right =

poj 3750 链表

扶醉桌前 提交于 2020-03-23 16:34:34
http://poj.org/problem?id=3750 小孩报数问题 好不容易写出了单链表版本,提交竟然out time limited。代码如下: #include <iostream> #include <string> using namespace std; typedef struct child { char name[20]; struct child* next; }; void child_delete(child* p) { child* _p = p->next; p->next = _p->next; free(_p); } int main() { int N,_N; child* head; child* tail; head = (child*)malloc(sizeof(child)); tail = head; tail->next = NULL; int flag = 1; int flag2 = 1; cin>>N; _N = N; while(N--) { child* stu; if(flag ==1) { stu = head; flag = 0; } else { stu = (child*)malloc(sizeof(child)); if(stu == NULL) return 0; } cin>>stu->name; stu

Java7 ConcurrentHashMap详解

五迷三道 提交于 2020-03-23 15:38:52
###Java7 ConcurrentHashMap ConcurrentHashMap 和 HashMap 思路是差不多的,但是因为它支持并发操作,所以要复杂一些。 整个 ConcurrentHashMap 由一个个 Segment 组成,Segment 代表”部分“或”一段“的意思,所以很多地方都会将其描述为分段锁。注意,行文中,我很多地方用了“槽”来代表一个 segment。 简单理解就是,ConcurrentHashMap 是一个 Segment 数组,Segment 通过继承 ReentrantLock 来进行加锁,所以每次需要加锁的操作锁住的是一个 segment,这样只要保证每个 Segment 是线程安全的,也就实现了全局的线程安全。 concurrencyLevel: 并行级别、并发数、Segment 数,怎么翻译不重要,理解它。默认是 16,也就是说 ConcurrentHashMap 有 16 个 Segments,所以理论上,这个时候,最多可以同时支持 16 个线程并发写,只要它们的操作分别分布在不同的 Segment 上。这个值可以在初始化的时候设置为其他值,但是一旦初始化以后,它是不可以扩容的。 再具体到每个 Segment 内部,其实每个 Segment 很像之前介绍的 HashMap,不过它要保证线程安全,所以处理起来要麻烦些。 ##初始化

Java8 ConcurrentHashMap详解

怎甘沉沦 提交于 2020-03-23 15:38:24
#Java8 ConcurrentHashMap Java7 中实现的 ConcurrentHashMap 说实话还是比较复杂的,Java8 对 ConcurrentHashMap 进行了比较大的改动。建议读者可以参考 Java8 中 HashMap 相对于 Java7 HashMap 的改动,对于 ConcurrentHashMap,Java8 也引入了红黑树。 说实话,Java8 ConcurrentHashMap 源码真心不简单,最难的在于扩容,数据迁移操作不容易看懂。 我们先用一个示意图来描述下其结构: 结构上和 Java8 的 HashMap 基本上一样,不过它要保证线程安全性,所以在源码上确实要复杂一些。 ##初始化 // 这构造函数里,什么都不干 public ConcurrentHashMap() { } public ConcurrentHashMap(int initialCapacity) { if (initialCapacity < 0) throw new IllegalArgumentException(); int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY : tableSizeFor(initialCapacity +

链表操作 -- 两个链表间的相交性问题

一世执手 提交于 2020-03-23 14:49:15
本文参考:    http://blog.csdn.net/g_brightboy/article/details/6824834    http://blog.csdn.net/huangxy10/article/details/8014233   在此致谢。 问题:   给定两个链表,判断它们是否相交。 注意:   如果两个链表相交的话,则公用相交点开始往后的所有节点。【因为节点的next指针只有一个!】 解答:   1)首先来看最直观的方法,也就是一次判断链表A中的节点是否在链表B中出现。   时间复杂度:O(n 2 )   空间复杂度:O(1)    1 bool List::isIntersect(Node* listA,Node* listB){ 2 for(Node* headA = listA; headA != NULL ; headA = headA->next){ 3 for(Node* headB = listB; headB != NULL; headB = headB->next){ 4 if(headA == headB) 5 return true; 6 } 7 } 8 return false; 9 } View Code      2)在方法(1)中,链表A中节点每次都需要匹配链表B中每个节点,这个匹配动作的时间复杂度为O(n)

Leetcode解题-链表(2.2.2)ReverseLinkedList

烈酒焚心 提交于 2020-03-23 14:48:24
题目: 2.2.2 Reverse Linked List II Reverse a linked list from position m to n. Do it in-place and in one-pass. For example: Given 1->2->3->4->5->nullptr, m = 2 and n = 4, return 1->4->3->2->5->nullptr. Note: Given m, n satisfy the following condition: 1 <= m <= n <= length of list. 链表旋转是个经典问题,可以锻炼我们操作链表的能力,值得好好学习一下。 我的方法:对 head 和 tail 特殊化处理 当 m<=i<=n 时,通过记录前继结点 prev ,简单的调转 next 指针方向,并记录 m 结点 pm 。最后迭代到 n+1 时,再处理 first ( pm->next )和 last ( p )结点的指向问题。 更好的方法:一般化处理 将整个过程一般化处理的方法为: 不断地将当前结点 cur 插入到 head 的后面 。(见下面图示)第一次将 3 插入到 1 后面,第二次将 4 插入到 1 后面,即 1=> 2=>3 =>4 旋转为 1=> 3=>2 =>4 。如果能解决的话,那么继续 1=> 3=>2

Leetcode解题-链表(2.2.3)PartitionList

笑着哭i 提交于 2020-03-23 14:45:19
题目: 2.2.3 Partition List Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x. You should preserve the original relative order of the nodes in each of the two partitions. For example, Given 1->4->3->2->5->2 and x = 3, return 1->2->2->4->3->5. 代码实现 我们可以分两步解决: 1) 遍历链表时,将其一分为二 :第一次添加结点,即创建分区时,记住两个分区的 header ( par1 和 par2 )。之后,用 par1p 和 par2p 指向分区的尾部,便于每次迭代向两个分区添加结点。 2) 链接两个分区 :将分区 2 链接到分区 1 的尾部,并将分区 2 最后一个结点的 next 赋值为 NULL (非常重要,后面会解释!)。 野指针引发的血案 指针 par1 , par2 都没有初始化为 NULL ,而后面的 if 却用 par1 或 par2 是否等于 NULL 作为初始化 par1h 和

堆和栈的区别(转过无数次的文章)

这一生的挚爱 提交于 2020-03-23 11:08:04
一、预备知识—程序的内存分配 一个由C/C++编译的程序占用的内存分为以下几个部分 1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其 操作方式类似于数据结构中的栈。 2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回 收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。 3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的 全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另 一块区域。 - 程序结束后由系统释放。 4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放 5、程序代码区—存放函数体的二进制代码。 二、例子程序 这是一个前辈写的,非常详细 //main.cpp int a = 0; 全局初始化区 char *p1; 全局未初始化区 main() { int b; 栈 char s[] = "abc"; 栈 char *p2; 栈 char *p3 = "123456"; 123456/0在常量区,p3在栈上。 static int c =0; 全局(静态)初始化区 p1 = (char *)malloc(10); p2 = (char *)malloc(20); 分配得来得10和20字节的区域就在堆区。