常用数据结构

大学打了四年游戏,没想到还要继续把数据结构捡起来。

一、数据结构概念
1、数组
  数组是可以再内存中连续存储多个元素的结构,在内存中的分配也是连续的,数组中的元素通过数组下标进行访问,数组下标从0开始。
2、栈
  栈是一种特殊的线性表,仅能在线性表的一端操作,栈顶允许操作,栈底不允许操作。 栈的特点是:先进后出 (先进去的数据是栈底)
3、队列
  队列与栈一样,也是一种线性表,不同的是,队列可以在一端添加元素,在另一端取出元素,也就是:先进先出(先进去的数据是队首)
4、链表
  链表是物理存储单元上非连续的、非顺序的存储结构,数据元素的逻辑顺序是通过链表的指针地址实现,
  每个元素包含两个结点,一个是存储元素的数据域 (内存空间),另一个是指向下一个结点地址的指针域
  根据指针的指向,链表能形成不同的结构,例如单链表,双向链表,循环链表等
5、树
  树是一种数据结构,它是由n(n>=1)个有限节点组成一个具有层次关系的集合
6、散列表
  散列表,也叫哈希表,是根据关键码和值 (key和value) 直接进行访问的数据结构,通过key和value来映射到集合中的一个位置,这样就可以很快找到集合中的对应元素
7、堆
  堆是一种比较特殊的数据结构,可以被看做一棵树的数组对象,具有以下的性质:堆中某个节点的值总是不大于或不小于其父节点的值;
  堆总是一棵完全二叉树。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。常见的堆有二叉堆、斐波那契堆等
8、图
  图是由结点的有穷集合V和边的集合E组成。其中,为了与树形结构加以区别,在图结构中常常将结点称为顶点,边是顶点的有序偶对,
  若两个顶点之间存在一条边,就表示这两个顶点具有相邻关系。

二、数据结构相关算法

1、链表

  a、链表初始化、头插法、尾插法

# 初始化链表
class Node:
    def __init__(self, item):
        self.item = item
        self.next = None

# 链表的创建
# 头插法:
def create_linklist_head(li):
    head = Node(li[0])  # 头结点
    for element in li[1:]:
        node = Node(element)  # 先把元素变为node对象
        node.next = head      # node对象的next属性设置为 头结点
        head = node           # 再把node对象置为头结点
    return head

# 尾插法:
def create_linklidt_tail(li):
    head = Node(li[0])
    tail = head
    for element in li[1:]:
        node = Node(element)
        tail.next = node
        tail = node
    return head
# 返回头结点因为头结点是第一个结点

# 遍历输出链表:
def print_linklist(li):
    while li:
        print(li.item, end=',')
        li = li.next
    print(end='\n')


li = create_linklist_head([1, 2, 3, 6, 8])
print_linklist(li)
lk = create_linklidt_tail([1, 2, 3, 6, 8])
print_linklist(lk)
View Code

  b、反转链表

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param head ListNode类 
# @return ListNode类
#
class Solution:
    def ReverseList(self , head: ListNode) -> ListNode:
        # write code here
        pre_head= None        # 反转链表的头节点
        while head:
            temp = head.next  # 先把head的next属性存起来
            
            head.next = pre_head   # 把head的next属性 变更为 pre_head
            pre_head = head    #  再把head置为反转链表的头结点
            # 第一轮pre_head为 val=1,next=None
            # 第二轮pre_head为 val=2,next=1
            # 第三轮pre_head为 val=3,next=2
            # 第四轮pre_head为 val=4,next=3
            
            head = temp  # 再把之前保存的next赋值给head,进入下一个循环 
            
        return pre_head
View Code

 2、二叉树前序、中序、后序遍历

前序遍历:根节点->左子树->右子树(根->左->右)
中序遍历:左子树->根节点->右子树(左->根->右)
后序遍历:左子树->右子树->根节点(左->右->根)

  a、二叉树的深度

# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param pRoot TreeNode类 
# @return int整型
#
class Solution:
    def TreeDepth(self , pRoot: TreeNode) -> int:
        # write code here
        if pRoot is None:
            return 0
        # 每max一次+1,max的次数等于递归深度,最终max(0,0)
        return max(self.TreeDepth(pRoot.left), self.TreeDepth(pRoot.right))+1
View Code

   b、二叉树的镜像

# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param pRoot TreeNode类 
# @return TreeNode类
#
class Solution:
    def Mirror(self , pRoot: TreeNode) -> TreeNode:
        # write code here
        if pRoot is None:
            return None;
        pRoot.left,pRoot.right = pRoot.right,pRoot.left;
        self.Mirror(pRoot.left)
        self.Mirror(pRoot.right)
        
        return pRoot
View Code

 3、用两个栈实现队列

class MyQueue(object):

    def __init__(self):
        self.stack1 = []
        self.stack2 = []

    def push(self, x):
        self.stack1.append(x)

    def pop(self):
        # 如果2为空,就把1的数据压到2; 
        # 不为空,就把2的数据删完
        if not self.stack2: 
            while self.stack1:
                self.stack2.append(self.stack1.pop())
        return self.stack2.pop() # 删除2最后一条数据 

    def peek(self):
        if not self.stack2: # 如果为空,就把1的数据压到2
            while self.stack1:
                self.stack2.append(self.stack1.pop())
        return self.stack2[-1] # 取2最后一条数据

    def empty(self):
        return not self.stack1 and not self.stack2

4、包含min函数的栈

# -*- coding:utf-8 -*-
class Solution:
    def __init__(self):
        self.stack1 = []
        self.stack2 = []
    def push(self, node):
        # write code here
        self.stack1.append(node)
    def pop(self):
        # return xx
        # 如果stack2有元素,则先pop。
        # 不这样做的话,会把最近一次的push给返回出去了,不符合先进先出原则
        if self.stack2:
            return self.stack2.pop()
        while self.stack1:
            self.stack2.append(self.stack1.pop())
        return self.stack2.pop()
View Code

 5、栈的弹出判断

#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
#
# @param pushV int整型一维数组
# @param popV int整型一维数组
# @return bool布尔型
#
class Solution:
    def IsPopOrder(self, pushV: List[int], popV: List[int]) -> bool:
        # write code here
        n = len(pushV)
        s = []  # 辅助栈
        j = 0  # 入栈下标
        for i in popV:  # 遍历出栈
            # 辅助栈为空或栈顶不等于出栈,则把数据填入辅助栈
            while (not s or s[-1] != i) and j < n:
                s.append(pushV[j])
                j += 1
            # 栈顶等于出栈,则删掉栈顶,进入下一个循环
            if s[-1] == i:
                s.pop()
            else:
                return False
        return True
View Code

 6、滑动窗口的最大值(双向队列)

#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param num int整型一维数组 
# @param size int整型 
# @return int整型一维数组
#
class Solution:
    def maxInWindows(self , num, size: int) :
        # write code here
        res = []
         #窗口大于数组长度的时候,返回空
        if size <= len(num) and size != 0:
            from collections import deque
            #双向队列
            dq = deque()

            # 先遍历一个窗口,取最大值的下标存进dp
            for i in range(size):
                while len(dq) != 0 and num[dq[-1]] < num[i]:
                     dq.pop()
                dq.append(i)

            # 遍历后续数组元素
            for i in range(size, len(num)):
                res.append(num[dq[0]])
                # 如果队首不在窗口内,则移除队首。直到队首满足在窗口内跳出循环
                while len(dq) != 0 and dq[0] < (i - size + 1):
                    dq.popleft()
                # 如果即将进入队列的下标的值(num[i] )大于队列后方的值,依次将小于的值拿出来去掉
                while len(dq) != 0 and  num[i] > num[dq[-1]]:
                    dq.pop()
                dq.append(i)

            # 最后一次循环没有把最后一组窗口的最大值加进数组,所以还需要加一次
            res.append(num[dq[0]])
        return res
View Code

7、给你一个整数数组nums、一个整数limit,要求子数组中任意两个元素差值小于等于limit,返回最长连续子数组的长度。【双指针】

class Solution:
    def longestSubarray(self, nums, limit: int) -> int:
        from sortedcontainers import SortedList
        # 创建一个有序数组,sortedcontainers模块需要安装
        s = SortedList()
        left, right, res = 0, 0, 0

        while right < len(nums):
            s.add(nums[right])

            # 如果最大最小值之差大于限制,有序数组就删除left指向的值,直到left满足条件
            # 有序数组如果有相同元素,只会删除第一个
            while s[-1] - s[0] > limit:
                s.remove(nums[left])
                left += 1

            # 当前子数组的长度1 + right - left ; 每循环一次,更新最大值
            res = max(res, 1 + right - left)
            right += 1

        return res
View Code

 

posted @ 2022-07-14 22:21  whitewall  阅读(39)  评论(0编辑  收藏  举报