程序员-这八种数据结构你必须懂

什么事数据结构?

数据结构是以某种特定的布局方式存储数据的容器。这种‘布局方式’决定了数据结构对于某些操作是高效的,而对于其他操作是低效的。首先我们需要理解各种数据结构,才能在实际处理问题时选择合适的数据结构。

下面我们重点分析一下常用的数据机构

  • 数组
  • 队列
  • 链表
  • 字典树
  • 散列表(哈希表)

一 数组

所谓数组 是有序的元素序列, 比如[1, 3, 4, 6] 数组长度为4,每个元素关联一个正数,我们称之为索引,它表明数组中每个元素所在的位置,大部分语言初始值索引定义为0

类型:

  • 一维数组
  • 多维数组

特点:

  • 在内存中数组是一块连续的区域
  • 数组需要预留空间,在使用前需要先申请内存的大小,可能会浪费内存空间
  • 插入数据和删除数据效率低,插入数据时,这个位置后面的数据在内存中都要向后移。
  • 随机读取效率很高。因为数组时连续的,知道每一个数据的内存地址,可以直接找到给地址的数据。
  • 不利扩展,数组定义的空间不够时需要重新定义数组。

二 栈

著名的撤销操作几乎遍布任意一个应用。但是你有没有思考过他是如何工作的呢?这个问题的解决思路时按照将最后的状态排列在先的顺序 ,在内存中存储历史工作状态(当然,他会受限一定的数量),这没办法用数组实现,但有了栈,就变的非常方便了。
可以把栈想象成一列垂直堆放的书,为了拿到中间的书,你需要移除放置在这上面的所有的书,这是后进先出的工作原理。


u=2845631122,2154696518&fm=173&app=25&f=JPEG.jpg
796da6488c298dcd907ee5e204d1215b.png

特点:

  • 数据的插入和删除只能通过一端实现,这一端称为栈顶,另一端栈底

实现:

栈的顺序存储和链式存储实际时栈的容器不同,一个数组实现 一个链表实现

  • 顺序存储实现


    cae536576ac76e5ac5f8cef45f7006d9.png
//栈的顺序存储:用数组来存放元素:数组0端为栈底;length-1端为栈顶
public class MySqStack {
    int[] data;//数组
    int top;//数组的下标

    public MySqStack(int size) {
        data = new int[size];
        top = -1;
    }

    // 进栈
    public boolean push(int element) {
        // 如果栈满,则不能插入
        if (top == data.length - 1) {
            // throw new RuntimeException("stack is empty");
            return false;
        } else {
            top++;
            data[top] = element;
            return true;
        }
    }

    // 出栈
    public int pop() {
        // 如果栈中没有元素
        if (top == -1) {
            throw new RuntimeException("stack is empty");
        } else {
            return data[top--];
        }
    }

    // 取栈顶元素
    public int GetTop() {
        // 如果栈中没有元素
        if (top == -1) {
            throw new RuntimeException("stack is empty");
        } else {
            return data[top];
        }
    }
  • 链式存储实现


    abb5e0f1fe566021a6872cc0b55582cb.png
public class LinkStack {
    class Node {
        int data;
        Node next;
    }
    // 创建头节点
    private Node header = new Node();

    // 新节点进栈:头插入法:插入头节点之后
    public boolean push(int data) {
        Node node = new Node();
        node.data = data;
        node.next = header.next;
        header.next = node;
        return true;
    }
    //出栈:将头节点之后的节点出栈
    public int pop() {
        if(header.next == null) {
            return -1;
        }
        else {
            int rtn = 0;
            rtn = header.next.data;
            header.next = header.next.next;
            return rtn;
        }
    }
    //取栈顶元素
    public int getTop() {
        if(header.next == null) {
            return -1;
        }
        else {
            return header.next.data;
        }
    }

应用:

安卓activity页面栈管理

三 队列

定义:

与栈相似,队列是一种顺讯存储元素的线形数据结构。栈与队列的最大差别在于栈是LIFO(后进先出)而队列是FIFO(先进先出)
一个完美的队列实现例子:销售亭排队,如果有新的人加入,它需要到队尾去排队,而非队首------排在前面的人会先拿到票,然后离开队伍。

1.png

实现:

class Queue(object):
    def __init__(self):
        self.items = []

    def enqueue(self,item):
        self.items.insert(0,item)
    
    def dequeue(self):
        return self.items.pop()
    
    def isEmpty(self):
        return self.items==[]
    
    def size(self):
        return len(self.items)

应用:

约瑟夫环问题(Josephus problem):n个人围成一圈,从1开始报数,每当有人报到m时,他被淘汰,下一个人继续从1开始报数,问最后的获胜者是谁?

Hot potato game:N个人编号1~N,围坐成一个圆圈。从1号人开始传递一个热土豆,经过M次传递后拿着热土豆的人被清除离座,由坐在后面的人拿起热土豆继续进行游戏。最后剩下的人获胜.
  上面两个问题相同,如果M=0和N=5,则游戏人依序被清除,5号游戏人获胜。如果M=1 和N=5,那么被清除的人的顺序是2,4,1,5,最后3号人获胜。

#n个人围成一圈,第一个人从1开始报数,每当有人报到m时,他被淘汰,下一个人继续从1开始报数
def josephus(people_list,num):
   q = Queue()
   for people in people_list:
       q.enqueue(people)

   while q.size()>1:
       for i in range(num-1):
           q.enqueue(q.dequeue())
       q.dequeue()
   return q.dequeue()
people_list = [1,2,3,4,5]
print josephus(people_list,7)

四 链表

定义:

链表是另一个重要的线性数据结构,乍一看可能有点像数组,但在内存分配、内部结构以及数据插入和删除的基本操作方面均有所不同。链表就像一个节点链,其中每个节点包含着数据和指向后续节点的指针。 链表还包含一个头指针,它指向链表的第一个元素,但当列表为空时,它指向null或无具体内容。
链表一般用于实现文件系统、哈希表和邻接表。

特点:

  • 在内存中可以存在任何地方,不要求连续
  • 每个数据保存下一个数据的内存地址,通过这个地址找到下一个数据。
  • 增加数据和删除数据比较容易。
  • 查找数据效率低,因为不具有随机访问醒,所以访问某个位置的数据都要从第一个数据开始访问,然后根据第一个数据保存的下一个数据的地址找到第二个数据,以此类推。(数组查找更快因为内存是连续的只要遍历地址就可以,可以直接找到对应地址的数据)
  • 不指定大小,扩展方便。链表大小不用定义,数据随意增删

类型:

  • 单链表
typedef struct node
{
    int val;
    struct node *next;
}Node;
  • 双向链表
typedef struct node
{
    int val;
    struct node *pre;
    struct node *next;
}Node;
20180724111803616.png

应用:

经典的链表应用场景: LRU 缓存淘汰算法
缓存是一种提高数据读取性能的技术,在硬件设计、软件开发中都有着非常广泛的应用,比如常见的CPU缓存、数据库缓存、浏览器缓存等等。
缓存空间的大小有限,当缓存空间被用满时,哪些数据应该被清理出去,哪些数据应该被保留?这就需要缓存淘汰策略来决定。常见的缓存清理策略有三种:先进先出策略FIFO(First In, First Out)、最少使用策略 LFU(Least Frequently Used)、最近最少使用策略LRU(Least Recently Used)。
如何用链表来实现LRU缓存淘汰策略呢?
思路:
维护一个有序单链表,越靠近链表尾部的结点是越早之前访问的。当有一个新的数据被访问时,我们从链表头部开始顺序遍历链表,如果此数据之前已经被缓存在链表中了,我们遍历得到这个数据的对应结点,并将其从原来的位置删除,并插入到链表头部。如果此数据没在缓存链表中,又可以分为两种情况处理:
如果此时缓存未满,可直接在链表头部插入新节点存储此数据;
如果此时缓存已满,则删除链表尾部节点,再在链表头部插入新节点。

五 树

定义:

树形结构是一种非线形,层级式的数据结构,由顶点(节点)和连接它们的边组成。 树类似于图,但区分树和图的重要特征是树中不存在环路。
树形结构被广泛应用于人工智能和复杂算法,它可以提供解决问题的有效存储机制

二叉树

每个结点至多拥有两棵子树(即二叉树中不存在度大于2的结点),并且,二叉树的子树有左右之分,其次序不能任意颠倒。


完满二叉树.png
完全二叉树.png
完美二叉树.png
4630295-96ab0ed90baf906e.png

B树

b树(balance tree)和b+树应用在数据库索引,可以认为是m叉的多路平衡查找树,但是从理论上讲,二叉树查找速度和比较次数都是最小的,为什么不用二叉树呢?
因为我们要考虑磁盘IO的影响,它相对于内存来说是很慢的。数据库索引是存储在磁盘上的,当数据量大时,就不能把整个索引全部加载到内存了,只能逐一加载每一个磁盘页(对应索引树的节点)。所以我们要减少IO次数,对于树来说,IO次数就是树的高度,而“矮胖”就是b树的特征之一,它的每个节点最多包含m个孩子,m称为b树的阶,m的大小取决于磁盘页的大小。

B+树

b+树,是b树的一种变体,也是一种多路搜索树,查询性能更好

B和B+树的区别在于,B+树的非叶子结点只包含导航信息,不包含实际的值,所有的叶子结点和相连的节点使用链表相连,便于区间查找和遍历。

推荐看张洋的 MySQL索引背后的数据结构及算法原理 这篇文章

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容