链表

数据结构与算法:链表

谁都会走 提交于 2020-02-26 10:29:37
一、链表和数组的区别 链表与数组相似,但链表是一种比数组稍微复杂的数据结构。数组需要一块连续的内存空间来存储数据,对内存的要求比较高,而链表则不需要,它通过「指针」将不连续的内存块串联起来。如果要申请一个 100MB 大小的数组和链表,当内存中没有连续的,或者没有足够大小的空间时,数组便会申请失败,而链表不会。 链表有很多种结构,常见的有: 单链表 、 双向链表 、 循环链表 。 二、单向链表 链表通过指针将不连续的内存块串联在一起使用,我们把其中的内存块称为「结点」,而为了将所有的结点串起来,链表中的结点除了存储数据之外,还会用指针记录链表结点的下一个结点的地址,我们把这个记录下一个结点的地址的指针叫做「后继指针next」,整个单链表如下图所示。 其中第一个结点和最后一个结点是比较特殊的,通常分别把它们称为「头结点」和「尾结点」。头结点用来记录链表的基地址,有了它就能遍历得到整个链表。尾结点并不指向任何结点,而是指向空地址 null ,表示链表上的最后一个结点。 与数组不同,链表的插入和删除操作并不需要大量的数据搬移,它只需要考虑相邻结点的指针改变,对应的时间复杂度为 O(1) 。同样,链表的访问元素操作也没有数组那样直接用首地址和下标通过寻址公式直接得到对应的内存地址,链表需要通过从指针一个一个结点地遍历,直到找到相应的结点。因此链表的随机访问时间复杂度为 O(n) 。 三

数据结构的基本概念

喜夏-厌秋 提交于 2020-02-26 08:15:27
数据结构 一、线性表 1. 顺序存储结构(顺序表) 一个线性表是n个具有相同特性的数据元素的有限序列。数据元素是一个抽象的符号,其具体含义在不同情况下不同。 2. 链表 链表里面节点的地址不是连续的,而是通过指针进行链接的。 二、哈希表 1. 哈希的导入 数组的特点是:寻址容易,插入和删除困难; 链表的特点是:寻址困难,插入和删除容易。 那么,我们综合两者的特性,做出一种寻址容易,插入和删除也容易的数据结构,这就是哈希表。 下面是哈希表的一种实现方法:拉链法。 在图的左边是一个数组,数组的每个成员包括一个指针,指向一个链表的头,根据元素的特征将元素分配到不同的链表中去,我们也是根据这些特征,找到正确的链表,再从链表中找到正确的元素。 2. 哈希表 哈希表就是把Key通过一个固定的算法函数——哈希函数,转换成一个整形数字,然后就将该数字对数长度进行取余,取余结果当作数组的下标,将value存储在以该数字为下表的数组空间里。 3. 哈希表的优缺点 优点 时间少:无论哈希表中的数据量,我们在操作时的插入,删除,查询都只需要O(1)的时间级。 速度快:在日常程序中,在特定时间内哈希表的查找速度总比树快。 编程简单:相对树来讲,哈希表编程实现相对简单。 缺点 哈希表基于数组,数组创建后难以扩展,故当哈希表被基本填满时,性能下降会非常严重,所以在创立哈希表时我们必须清楚表中将要存储多少数据。

[leetcode]Swap Nodes in Pairs @ Python

十年热恋 提交于 2020-02-26 05:29:11
原题地址:http://oj.leetcode.com/problems/swap-nodes-in-pairs/ 题意:将链表中的节点两两交换。 Given 1->2->3->4 , you should return the list as 2->1->4->3 . 解题思路:这题主要涉及到链表的操作,没什么特别的技巧,注意不要出错就好。最好加一个头结点,操作起来会很方便。 代码: # Definition for singly-linked list. # class ListNode: # def __init__(self, x): # self.val = x # self.next = None class Solution: # @param a ListNode # @return a ListNode def swapPairs(self, head): if head == None or head.next == None: return head dummy = ListNode(0); dummy.next = head p = dummy while p.next and p.next.next: tmp = p.next.next p.next.next = tmp.next tmp.next = p.next p.next = tmp p = p

看一遍就理解,图解单链表反转

眉间皱痕 提交于 2020-02-26 05:24:14
前言 反转链表是程序员必备的基本素养,经常在面试、笔试的过程中出现。一直觉得反转链表实现代码不是很好理解,决定搬leetcode那道经典反转链表题出来,用十多张图去解析它,希望加深大家对链表反转的理解,谢谢阅读。 leetcode的反转链表原题&答案 题目描述: 反转一个单链表。 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL 分析: 假设存在链表 1 → 2 → 3 → Ø,我们想要把它改成 Ø ← 1 ← 2 ← 3。 在遍历列表时,将当前节点的 next 指针改为指向前一个元素。由于节点没有引用其上一个节点,因此必须事先存储其前一个元素。在更改引用之前,还需要另一个指针来存储下一个节点。不要忘记在最后返回新的头引用! public ListNode reverseList(ListNode head) { ListNode prev = null; ListNode curr = head; while (curr != null) { ListNode nextTemp = curr.next; curr.next = prev; prev = curr; curr = nextTemp; } return prev; } 图解链表反转代码的实现 接下来,我们图解以上代码实现,先对以上实现代码加上行号,如下: public

142. 环形链表 II

不打扰是莪最后的温柔 提交于 2020-02-26 04:58:11
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。 说明:不允许修改给定的链表。 代码实现: /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode *detectCycle(ListNode *head) { ListNode *low = head; ListNode *fast = head; ListNode *meet = NULL; while(fast) { fast = fast->next; low = low->next; if(!fast) return NULL; fast = fast->next; if(low == fast) { meet = fast; break; } } if(meet == NULL) return NULL; while(head && meet) /

链表逆置合集

 ̄綄美尐妖づ 提交于 2020-02-26 02:51:19
链表逆置 #include<bits/stdc++.h> using namespace std; struct LNode { int data; LNode *next; }; void creathead(LNode *&L,int a[],int n)///尾插法 { LNode *r,*s; L=(LNode*)malloc(sizeof(LNode)); L->next=NULL; r=L; for(int i=0; i<n; i++) { s=(LNode*)malloc(sizeof(LNode)); s->data=a[i]; r->next=s; r=r->next; } r->next=NULL; } void reverse(LNode *&L) { LNode *p,*q; p=L->next; L->next=NULL; while(p!=NULL) { q=p; p=p->next; q->next=L->next; L->next=q; } } int main() { LNode *L,*B; int n,c[10000]; scanf("%d",&n); for(int i=0; i<n; i++) scanf("%d",&c[i]); creathead(L,c,n); reverse(L); while(L->next!=NULL) {

链表面试题小结

家住魔仙堡 提交于 2020-02-26 02:28:30
某本书上面说了,链表这个东西,实际用的并不多,但是可以提供很好的考察面试者编程技巧和思维能力的素材。这里总结一下,见过的面试题和对应的候选解法。 题一、给定单链表,检测是否有环。 使用两个指针p1,p2从链表头开始遍历,p1每次前进一步,p2每次前进两步。如果p2到达链表尾部,说明无环,否则p1、p2必然会在某个时刻相遇(p1==p2),从而检测到链表中有环。 http://ostermiller.org/find_loop_singly_linked_list.html 这篇文章讲了很多好的坏得相关算法。 题二、 给定两个单链表(head1, head2),检测两个链表是否有交点,如果有返回第一个交点。 如果head1==head2,那么显然相交,直接返回head1。 否则,分别从head1,head2开始遍历两个链表获得其长度len1与len2。假设len1>=len2,那么指针p1由head1开始向后移动len1-len2步。指针p2=head2,下面p1、p2每次向后前进一步并比较p1p2是否相等,如果相等即返回该结点,否则说明两个链表没有交点。 题三、 给定单链表(head),如果有环的话请返回从头结点进入环的第一个节点。 运用题一,我们可以检查链表中是否有环。 如果有环,那么p1p2重合点p必然在环中。从p点断开环,方法为:p1=p, p2=p->next, p-

Skr-Eric的数据结构课堂(一)-- 概述、线性结构的基本实现模型、容器和二叉树结构

半城伤御伤魂 提交于 2020-02-26 02:07:29
数据结构概述 概念: 数据结构:计算机存储和组织数据的方式. 分为三层: 抽象层 (逻辑结构):数据之间(与计算机无关)的逻辑结构. 分为: 线性结构/非线性结构 其中,非线性结构分为:集合结构/树状结构/网状结构. A.线性结构(有序性/一对一的前后关系) 结构中必须存在唯一的首元素 结构中必须存在唯一的尾元素 除首元素外,结构中每一个元素有且只有一个前驱元素 除尾元素外,结构中每一个元素有且只有一个后继元素 B.集合结构(无序性/松散的) 确定性:要么属于集合要么不属于集合,不存在可能属于或者可能不属于的情况. 无序性:无前后顺序关系. 唯一性:各元素互不相同. C.树状结构(一对多的父子关系) 结构中必须存在唯一的根节点 除根元素外,结构中每一元素有且只有一个前驱元素 除叶元素外,结构中每一元素可拥有一个或多个后继元素 D.网状结构(多对多的映射关系) 结构中每一元素都可拥有任意数量的前驱元素和后继元素 结构中任意两个元素之间均可建立关联 逻辑层 (物理结构):数据具体在计算机内存中存储方式. 1.顺序存储结构:把逻辑上相邻的元素存储在相邻的物理内存位置中. 优缺点:节省内存空间/方便随机访问/增加或删除元素复杂/降低存储空间的利用率 2.链式存储结构:把逻辑上相邻的元素存储放在不连续的物理内存位置中,通过存放下一数据的链接域连接起来. 优缺点:浪费内存空间/随机访问不方便

21.javase-javaAPI-集合之map集合

情到浓时终转凉″ 提交于 2020-02-26 00:31:33
Map map是一种key,value结构,key是键,value是值,也就是键值对结构. 这是Map的继承体系 HashMap(数组+链表+红黑树) 特点: 1.键值对,<key,value>结构 2.key值是根据键的hash值计算的,并且键是唯一的. 3.value是任意类型的. 4.底层是数组加链表的形式.当key’的hash值相同时,发生hash碰撞,再用equals()方法判断是否相同,如果相同则覆盖,如果不同则生成链表.当链表长度大于8时,链表变成红黑树. 5.hashMap为线程不安全的数据结构.如果多线程可能造成hash碰撞时链表中的两个节点互相指定.形成循环链. jdk1.7时hashMap的实现 大方向上,HashMap 里面是一个数组,然后数组中每个元素是一个单向链表。上图中,每个绿色的实体是嵌套类 Entry 的实例,Entry 包含四个属性:key, value, hash 值和用于单向链表的 next。 capacity:当前数组容量,始终保持 2^n,可以扩容,扩容后数组大小为当前的 2 倍。 loadFactor:负载因子,默认为 0.75。 threshold:扩容的阈值,等于 capacity * loadFactor jdk1.8时hashMap的实现 最大的不同就是利用了红黑树,所以其由 数组+链表+红黑 树 组成。 根据 Java7

ConcurrentHashMap1.8源码

我的梦境 提交于 2020-02-26 00:08:56
/** * 初始化数组table, * 如果sizeCtl小于0,说明别的数组正在进行初始化,则让出执行权 * 如果sizeCtl大于0的话,则初始化一个大小为sizeCtl的数组 * 否则的话初始化一个默认大小(16)的数组 * 然后设置sizeCtl的值为数组长度的3/4 */ private final Node<K,V>[] initTable() { Node<K,V>[] tab; int sc; while ((tab = table) == null || tab.length == 0) { //第一次put的时候,table还没被初始化,进入while if ((sc = sizeCtl) < 0) //sizeCtl初始值为0,当小于0的时候表示在别的线程在初始化表或扩展表 Thread.yield(); // lost initialization race; just spin else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) { //SIZECTL:表示当前对象的内存偏移量,sc表示期望值,-1表示要替换的值,设定为-1表示要初始化表了 try { if ((tab = table) == null || tab.length == 0) { int n = (sc > 0) ? sc :