常见数据结构及算法

本文详细介绍了数据结构中的数组、栈、队列、链表、树和散列表,以及排序算法如冒泡排序、选择排序,还有递归算法、二分查找等基础算法概念、性质和应用。这些知识在计算机科学中至关重要,是理解和解决复杂问题的基础。
摘要由CSDN通过智能技术生成

一、常见的数据结构

        1、数组:

                数组是可以再内存中连续存储多个元素的结构,在内存中的分配也是连续的,数组中的元素通过数组下标进行访问,数组下标从0开始。

        2、栈

                 栈是一种特殊的线性表,仅能在线性表的一端操作,栈顶允许操作,栈底不允许操作。 栈的特点是:先进后出,或者说是后进先出,从栈顶放入元素的操作叫入栈,取出元素叫出栈。栈的结构就像一个集装箱,越先放进去的东西越晚才能拿出来,所以,栈常应用于实现递归功能方面的场景,例如斐波那契数列。

        3、队列

                队列与栈一样,也是一种线性表,不同的是,队列可以在一端添加元素,在另一端取出元素,也就是:先进先出。从一端放入元素的操作称为入队,取出元素为出队。

        4、链表

                链表是物理存储单元上非连续的、非顺序的存储结构,数据元素的逻辑顺序是通过链表的指针地址实现,每个元素包含两个结点,一个是存储元素的数据域 (内存空间),另一个是指向下一个结点地址的指针域。根据指针的指向,链表能形成不同的结构,例如单链表,双向链表,循环链表等。

        5、树

                树是一种数据结构,它是由n(n>=1)个有限节点组成一个具有层次关系的集合。把它叫做 “树” 是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。

                1、特点:

                        每个节点有零个或多个子节点;
                        没有父节点的节点称为根节点;
                        每一个非根节点有且只有一个父节点;
                        除了根节点外,每个子节点可以分为多个不相交的子树;

                2、分类

                        无序树:树中任意节点的子结点之间没有顺序关系,这种树称为无序树,也称为自由树;
                        有序树:树中任意节点的子结点之间有顺序关系,这种树称为有序树;
                        二叉树:每个节点最多含有两个子树的树称为二叉树;
                        满二叉树:叶节点除外的所有节点均含有两个子树的树被称为满二叉树;
                        完全二叉树:除最后一层外,所有层都是满节点,且最后一层缺右边连续节点的二叉树称为完全二叉树;
                        哈夫曼树(最优二叉树):带权路径最短的二叉树称为哈夫曼树或最优二叉树。

        6、 散列表

                散列表,也叫哈希表,是根据关键码和值 (key和value) 直接进行访问的数据结构,通过key和value来映射到集合中的一个位置,这样就可以很快找到集合中的对应元素。

        7、堆

               1、 概念:

                        堆是一种比较特殊的数据结构,可以被看做一棵树的数组对象

               2、 性质:

                        堆中某个节点的值总是不大于或不小于其父节点的值;
                        堆总是一棵完全二叉树。

        8、图

                        图是由结点的有穷集合V和边的集合E组成。其中,为了与树形结构加以区别,在图结构中常常将结点称为顶点,边是顶点的有序偶对,若两个顶点之间存在一条边,就表示这两个顶点具有相邻关系


        

二、常用算法

1.递归算法

           1、概念:

                递归算法是一种直接或者间接调用自身函数或者方法的算法。说简单了就是程序自身的调用。递归算法就是将原问题不断分解为规模缩小的子问题,然后递归调用方法来表示 问题的解。(用同一个方法去解决规模不同的问题)

递归算法,顾名思义就是有两个大的阶段:递和归,即就是有去(递去)有回(归来)。

  • 递去:将递归问题分解为若干个规模较小,与原问题形式相同的子问题,这些子问题可以用相同的解题思路来解决
  • 归来:当你将问题不断缩小规模递去的时候,必须有一个明确的结束递去的临界点(递归出口),一旦达到这个临界点即就从该点原路返回到原点,最终问题得到解决

        2、设计要素:       

                1、明确递归终止条件

                2、提取重复逻辑,缩小问题规模不断递去

                3、给出终止时的处理方法

        3、实际应用

                1、阶乘

                2、斐波那契数列

                3、hanoi塔问题

                4、树

2、排序算法

        1、冒泡排序: 

              1、概念“冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法

              它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行,直到没有相邻元素需要交换,也就是说该元素列已经排序完成。

这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”

                2、冒泡时间复杂度 O(N^2)

                        两层循环n*(n-1)

                3、冒泡代码实现       

def arrsort(arr):
    n = len(arr)
    for i in range(0, n -1):
        for j in range(i + 1, n):
            if arr[i] > arr[j]:
                arr[i], arr[j] = arr[j], arr[i]
    return arr

a = arrsort([3,4,1,9,6])
print(a)

        2、 选择排序

               1、概念:

                        选择排序是一种简单直观的排序算法,无论什么数据进去都是 O(n²) 的时间复杂度。所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。具体来说,假设长度为n的数组arr,要按照从小到大排序,那么先从n个数字中找到最小值min1,如果最小值min1的位置不在数组的最左端(也就是min1不等于arr[0]),则将最小值min1和arr[0]交换,接着在剩下的n-1个数字中找到最小值min2,如果最小值min2不等于arr[1],则交换这两个数字,依次类推,直到数组arr有序排列。算法的时间复杂度为O(n^2)
             2、原理

                1.首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。

                ​2.再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。

                ​3.重复第二步,直到所有元素均排序完毕。

              3、选择排序与冒泡的区别    

                       1、 冒泡排序的交换操作发生在每一轮比较中,而选择排序的交换操作发生在每一轮结束后。冒泡排序会在每一轮中将当前未排序部分的最大值移动到末尾,而选择排序会在每一轮结束后将当前未排序部分的最小值移动到起始位置。

                        2、冒泡排序的时间复杂度为O(n^2),无论数据集是否有序,每一轮都需要进行完整的比较和交换操作。而选择排序在数据集无序的情况下时间复杂度为O(n^2),但在数据集有序的情况下时间复杂度为O(n),因为只需要进行n-1次比较操作,而交换操作也可以通过记录最小值位置来避免不必要的交换。

                        综上所述,如果数据集无序,选择排序可能比冒泡排序更快;如果数据集有序,选择排序的时间复杂度更稳定,不受数据集状态的影响。但在大多数情况下,基于比较的排序算法的时间复杂度都是O(n^2),不太适合处理大规模数据集

                4、选择排序代码实现

def selection_sort(arr):
    """
    选择排序算法实现
    :param arr: 待排序数组
    """
    n = len(arr)
    for i in range(n-1):
        # 找到当前未排序部分的最小值
        min_idx = i
        for j in range(i+1, n):
            if arr[j] < arr[min_idx]:
                min_idx = j
        # 将最小值与当前位置进行交换
        arr[i], arr[min_idx] = arr[min_idx], arr[i]

3、二分查找算法

        1、概念:

                对于n个有序且没有重复的元素(假设为升序),从中查找特定的某个元素x,我们可以将有序序列分成规模大致相等的两部分,然后取中间元素与要查找的元素x进行比较,如果x等于中间元素,则查找成功,算法终止;如果x小于中间元素,则在序列的前半部分继续查找,否则在序列的后半部分继续查找。这样就可以将查找的范围缩小一半,然后在剩余的一半中继续重复上面的方法进行查找。


        2、应用:

                排序算法中的快速排序、归并排序,数据结构中的二叉树、堆、线段树等。二分是一种常用且高效的算法,它的基本用途是在单调序列中进行查找和判定操作。

        3、时间复杂度:O (log N)

        4、代码实现Pyhton

def binaty_search(arr, val):
    low = 0
    high = len(arr) -1
    while low <= high:
        mid = (low + high) // 2
        if arr[mid] == val:
            return mid
        elif arr[mid] > val:
            high = mid -1
        else:
            low = mid + 1
    return None

arr = list(range(1, 10))
print(arr)
ret = binaty_search(arr, 4)
print(ret)

4、搜索算法

5、散列哈希算法

        散列哈希算法(Hashing)是一种将任意长度的输入数据映射为固定长度输出数据的算法。这种映射关系通常是非常复杂的,具有不可逆性,即不同的输入数据会得到不同的输出值,但同一个输入数据每次都会得到相同的输出值,这一特性使得散列哈希算法被广泛应用于密码学、数据完整性验证、数据分片和散列表等领域。

散列哈希算法的核心思想是将输入数据转换成一个定长的哈希值,这个哈希值可以作为数据的唯一标识。通常情况下,哈希值的长度是固定的,比如MD5算法生成的哈希值长度为128位,SHA1算法生成的哈希值长度为160位,SHA256算法生成的哈希值长度为256位等。

在实际应用中,散列哈希算法通常需要满足以下几个要求:

  1. 易于计算:对于任意长度的输入数据,哈希值的计算都应该是高效且快速的。
  2. 唯一性:不同的输入数据应该生成不同的哈希值,也就是哈希冲突的概率应该尽可能小。
  3. 防篡改:对于输入数据的任何修改,都应该能够显著地影响哈希值,以防止数据篡改。
  4. 散列性:哈希值的分布应该尽可能均匀,以保证在散列表等数据结构中的查找效率。

常见的散列哈希算法包括MD5、SHA1、SHA256等。虽然散列哈希算法可以保证数据的唯一性和完整性,但也存在一定的安全风险,因此在密码学和安全领域中,通常需要采用更加复杂和安全的加密算法来保护数据的安全

6、贪心算法

        1、概念:

               贪心算法(Greedy Algorithm)是一种基于贪心策略的算法,它在每一步选择中都采取当前状态下最优的选择,以期达到全局最优解。贪心算法通常适用于求解优化问题,例如求解最小生成树、最短路径、背包问题、调度问题等。与动态规划算法相比,贪心算法通常更加简单、高效,但是不能保证得到全局最优解。

                贪心算法的基本思路是:在每一步选择中,都选择当前状态下最优的解决方案,即局部最优解,并且希望通过这些局部最优解达到全局最优解。在实际应用中,通常需要满足以下两个条件:无后效性:即某个状态下的决策不会影响之后的状态。贪心选择性:即某个状态下的最优决策一定包含之后的最优决策,即贪心选择性。如果问题满足这两个条件,那么就可以考虑使用贪心算法求解。

        2、优点

                其简单、高效,并且不需要预处理或者保存中间状态,因此在一些实际问题中,贪心算法可以作为一种解决问题的有效手段。但是贪心算法也有其局限性,因为它只考虑当前状态下的最优解,可能会忽略一些潜在的更优解,因此在设计贪心算法时需要仔细分析问题的性质,并且选择合适的贪心策略。

        3、应用场景         

                1、零钱兑换问题

                假设有若干种不同面值的硬币,例如1元、2元、5元、10元等,现在需要找零m元,问最少需要多少个硬币?这个问题可以用贪心算法来求解。具体做法是每次尽可能选择面值最大的硬币,直到找零数为0为止。

                2、区间覆盖问题

                给定一个数轴上的若干个区间,要求选择尽可能少的区间,使得这些区间的并覆盖整个数轴。这个问题可以用贪心算法来求解。具体做法是按照右端点从小到大排序,每次选择右端点最小的区间,并将它覆盖的部分从数轴上去掉,直到数轴上没有未覆盖的部分为止。

                3、背包问题

                背包问题是指给定一个容量为C的背包,以及若干个重量为w,价值为v的物品,要求在不超过背包容量的情况下,选择一些物品放入背包,使得放入背包的物品总价值最大。这个问题可以用贪心算法来求解。具体做法是按照单位价值(即价值除以重量)从大到小排序,每次尽可能选择单位价值最大的物品放入背包,直到背包容量不足为止。

                4、其他场景:例如最小生成树、最短路径、调度问题等等,只要问题满足贪心选择性和无后效性的条件,都可以尝试使用贪心算法来求解。        

7、分治算法

        1、概念:

        分治算法(Divide and Conquer)是一种将问题分成多个子问题,然后分别解决子问题,最终合并子问题解得到原问题解的算法思想。分治算法的核心思想是将一个大问题分解成若干个小问题,然后分别解决这些小问题,最后将小问题的解合并起来得到大问题的解。

        2、步骤如下:

                分解(Divide):将原问题分成若干个子问题,每个子问题的结构与原问题相同,只是规模更小。

                解决(Conquer):递归地解决子问题。如果子问题足够小,则直接求解。

                合并(Combine):将子问题的解合并成原问题的解。

        3、分治算法的经典应用

                1、排序问题 快排 时间复杂度为O(nlogn),是一种效率较高的排序算法

                        1、选择一个枢轴元素(通常是第一个或最后一个元素)。

                        2、将数组分成两个部分,左边部分的元素都小于等于枢轴元素,右边部分的元素都大于等于枢轴元素。

                        3、对左右两个部分分别进行递归排序。

                2、矩阵乘法

                3、快速傅里叶变换

                4、 其他的应用,例如归并排序、二分搜索、最近点对问题等等。

8、回溯算法

        1、概念:

                回溯算法是一种在搜索算法中常用的技术,通常用于在大规模的搜索空间中寻找所有的可行解或最优解。回溯算法通过不断地尝试各种可能的选择,如果发现当前的选择不是可行解或者不是最优解,就回溯到前面的状态,尝试其他的选择,直到找到可行解或最优解为止。        

        2、一般步骤:

                1、选择一个候选项。
                2、检查候选项是否符合条件,如果符合,则进入下一层,如果不符合,则回溯到上一层,尝试其他的候选项。
                3、当回溯到最后一层时,找到了一组解,保存并返回。

        3、应用场景:

                求解排列、组合等问题。
                求解迷宫、棋盘等搜索问题。
                求解求和、集合覆盖等问题。

        4、经典用法:八皇后

                放置8个皇后,使得任意两个皇后都不能在同一行、同一列或同一对角线上。这个问题可以通过回溯算法求解,具体步骤是逐行搜索棋盘,对于每一行,枚举该行每一个格子放置皇后或不放置皇后,然后进入下一行搜索。如果当前的状态不符合要求,则回溯到上一行,重新选择候选项。最终,如果找到了一组可行解,则输出结果。

9、动态规划(DP)算法

        pass

10、字符串匹配算法

        1、概念:

                字符串匹配算法是指在文本串中查找一个模式串的过程。常见的字符串匹配算法有暴力匹配算法、KMP 算法、Boyer-Moore 算法等

        2、常见字符串匹配算法:

                1、暴力匹配算法(朴素算法)

                        暴力匹配算法是一种朴素的匹配算法,它的思路是从文本串的第一个字符开始,逐个字符地与模式串进行匹配,如果匹配成功则继续匹配下一个字符,如果匹配失败则将文本串向右移动一位,重新从该位置开始匹配。该算法的时间复杂度为 O(mn),其中 m 和 n 分别是模式串和文本串的长度。

                2、KMP 算法

                        KMP 算法是一种优化的字符串匹配算法,它的核心思想是利用已经匹配过的信息来尽可能减少模式串与文本串的匹配次数。具体实现中,KMP 算法维护一个 next 数组,用于记录模式串中每个前缀字符串的最长相等前缀后缀的长度,然后利用这个信息在匹配过程中尽可能减少模式串的后移次数,从而提高匹配效率。KMP 算法的时间复杂度为 O(m+n),其中 m 和 n 分别是模式串和文本串的长度。

                3、Boyer-Moore 算法

                        Boyer-Moore 算法是一种更为高效的字符串匹配算法,它的核心思想是从模式串的末尾开始,逆序匹配文本串中的字符,当发现不匹配的字符时,根据预处理好的规则跳过一定长度的字符进行下一次匹配。具体实现中,Boyer-Moore 算法维护两个规则:坏字符规则和好后缀规则,分别用于在发现不匹配字符时跳过一定长度的文本串和模式串。Boyer-Moore 算法的时间复杂度为 O(mn),但在实际应用中表现得非常优秀。

        3、字符串匹配算法应用场景

               1、文本编辑器中的查找和替换、搜索引擎中的搜索功能

                2、网络协议中的数据包过滤和匹配

        


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值