数据结构与算法Python版(ing)

可以看这个特别好的:《数据结构与算法基础》教学视频目录

1.单向链表


单链表节点一个类,单链表一个类,单链表这个类中的元素都是节点类的。
单链表的操作:

  • is_empty() 链表是否为空
    取cur指向链表的首节点,判断是不是None

  • length() 链表长度
    取cur指向链表的首节点,设置count用来计数,当前指向若不为None,则计数+1,移动cur指向下一个节点。

  • travel() 遍历整个链表
    思路同链表长度,把计数的部分换成print

  • first_add(item) 链表头部添加元素



    新添加的元素需要定义成节点类的一个实例。新添加的元素就是头节点,新添加节点的下一个指向原链表的头节点。把链表的头节点指向新添加的节点。

  • last_add(item) 链表尾部添加元素
    新添加的元素需要定义成节点类的一个实例。
    判断当前链表是不是为空,如果是空,那这个链表的头节点指向新添加的节点。
    如果不是空,那就将cur指向头节点,按照travel()的思路遍历这个链表,一直走到最后一个节点,将最后一个节点的下一个元素设置为新添加的这个节点。注意判断是否最后一个节点的条件。

  • insert(pos, item) 指定位置添加元素



    新添加的元素需要定义成节点类的一个实例。
    判断pos的值,如果pos<=0,则相当于在首部插入,调用首部插入的方法。
    如果pos的值>链表长度-1(注意这里pos的值是从0开始的),则相当于尾部插入,调用尾部插入的方法。
    否则:定义pre指向当前链表的头节点,用一个count计算当前指向的索引值,移动cur直到走到pos-1的位置,将新节点的下一个元素指向原链表这个位置的下一个元素,原链表的下一个位置指向新节点。见下图


  • remove(item) 删除节点



    定义两个指针,pre一开始是None,cur 指向原链表的头节点。如果删除的是头节点,那么把链表的头节点指向原链表头节点的下一个节点。如果不是头节点,就将pre指向当前cur的节点,cur的指向一个一个的往后挪,(这样就相当于pre在后,cur在前)直到cur指向的值就是我们要删除的值,把pre的下一个节点指向cur的下一个节点。

  • search(item) 查找节点是否存在
    思路同travel()


class Node(object):
    def __init__(self,item):
        self.item = item
        self._next = None



class singleLinkList(object):
    def __init__(self):
        # 头节点
        self._head = None
    def is_empty(self):
        return self._head == None

    def length(self):
        # 首先要指向头节点
        cur = self._head
        count = 0
        while cur is not None:
            count = count+1
            cur = cur._next
        return count

    def travel(self):
        if self.is_empty():
            return
        # 首先要指向头节点
        cur = self._head
        while cur != None:
            print(cur.item)
            cur = cur._next
        print("")
    def first_add(self,elem):
        # 把新添加的数定义到Node类里,这样它就会有item和_next两个属性
        p = Node(elem)
        p._next = self._head
        # 把链表的头节点指向新添加的节点
        self._head = p


    def last_add(self,elem):
        cur = self._head
        p = Node(elem)
        # 首先判断这个链表是不是空的
        if self.is_empty():
            self._head = p
        else:
            cur = self._head
            while cur._next != None:
                cur = cur._next
            cur._next = p
    def insert(self,index,elem):
        # 如果插入的地方在第一个位置之前,直接调用在首部插入的方法
        if index<=0 :
            self.first_add(elem)
        # 如果插入的地方在最后一个位置之后,直接调用在尾部插入的方法
        elif index>(self.length()-1):
            self.last_add(elem)
        else:
            p = Node(elem)
            count = 0
            pre = self._head
            while count<(index-1):
                pre = pre._next
                count = count+1
            p._next = pre._next
            pre._next = p

    def remove(self,elem):
        cur = self._head
        pre = None
        while cur is not None:
            if cur.item == elem:
                #  如果第一个就是要删除的节点
                if not pre:
                    self._head = cur._next
                else:
                    pre._next = cur._next
                break
            else:
                # 将删除位置前一个节点的next指向删除位置的后一个节点
                pre = cur
                cur = cur._next

    def search(self,elem):
        cur = self._head
        index = 0
        while cur is not None:
            if cur.item == elem:
                return index
            else:
                cur = cur._next
                index = index+1
        return index
if __name__ == "__main__":
    ll = singleLinkList()
    print("-------创建链表,首部加1,首部加2---------")
    ll.first_add(1)
    # print(ll.travel())
    ll.first_add(2)
    ll.travel()

    print("-------尾部添加3---------")
    ll.last_add(3)
    ll.travel()

    print("-------在索引为2的位置添加4---------")
    ll.insert(2, 4)
    print("length:",ll.length())
    ll.travel()

    print("-------查找链表中某一个数的索引---------")
    index = ll.search(2)
    print(index)

    print("-------删除链表中某一个数---------")
    ll.remove(3)
    # print "length:",ll.length()
    ll.travel()

2.循环单链表

  • 首部添加元素



    首先判断这个链表是不是空,如果是空,把头指针指向新添加的这个p节点,并且p节点的下一节点指向头节点。(因为是循环的)
    如果不是空,那么先把p节点的下一个节点指向头节点,然后往后走,找到最后一个节点,把原链表的最后一个节点的下一个节点指向新加入的p节点,并且把头指针指向这个p节点(因为是首部加入)

  • 尾部添加元素
    首先判断原链表是不是空的,如果是空的,就按照上面的,把头指针(self._head)指向新添加的这个p节点,并且p节点的下一节点指向头节点。
    如果不是空,就找到原链表的最后一个节点,将最后一个节点的下一节点指向p节点,然后p节点的下一个节点指向头节点,头指针(self._head)没变。
  • remove方法还需要好好看看。

class Node(object):
    def __init__(self,item):
        self.item = item
        self._next = None



class circularLinkList(object):
    def __init__(self):
        # 头节点
        self._head = None
    def is_empty(self):
        return self._head == None

    def length(self):
        # 首先要指向头节点
        cur = self._head
        count = 0
        while cur is not None:
            count = count+1
            cur = cur._next
        return count


    def travel(self):
        if self.is_empty():
            return
        # 首先要指向头节点
        cur = self._head

        while cur._next != self._head:
            print(cur.item)
            cur = cur._next
        print(cur.item)
        print("")


    def first_add(self,elem):
        # 把新添加的数定义到Node类里,这样它就会有item和_next两个属性
        p = Node(elem)
        if self.is_empty():
            self._head = p
            p._next = self._head
        else:
            # 首先将新加入的这个节点的下一个节点指向首节点
            p._next = self._head
            # 然后将原链表的尾节点指向这个新加入的节点
            cur = self._head
            while cur._next != self._head:
                cur = cur._next
            cur._next = p
            self._head = p


    def last_add(self,elem):

        p = Node(elem)
        # 首先判断这个链表是不是空的
        if self.is_empty():
            self._head = p
            p._next = self._head
        else:
            # 直接走到最后的节点
            cur = self._head
            while cur._next != self._head:
                cur = cur._next
            cur._next = p
            p._next = self._head



    def insert(self,index,elem):
        # 如果插入的地方在第一个位置之前,直接调用在首部插入的方法
        if index<=0 :
            self.first_add(elem)
        # 如果插入的地方在最后一个位置之后,直接调用在尾部插入的方法
        elif index>(self.length()-1):
            self.last_add(elem)
        else:
            p = Node(elem)
            count = 0
            pre = self._head
            while count<(index-1):
                pre = pre._next
                count = count+1
            p._next = pre._next
            pre._next = p

    def remove(self,elem):
        # 如果原链表为空
        if self.is_empty():
            return

        cur = self._head
        pre = None

        # 如果链表的第一个元素就是要删除的元素
        if cur.item == elem:
            # 如果链表多于一个节点,就找到最后一个节点
            if cur._next != self._head:
                while cur._next != self._head:
                    cur = cur._next
                cur._next = self._head._next
                self._head = self._head._next
            else:
                # 如果链表只有一个元素,就直接置空
                self._head = None
        else:
            pre = self._head
            while cur._next != self._head:
                # 如果找到了要找的元素
                if cur.item == elem:
                    pre._next = cur._next
                    return
                else:
                    pre = cur
                    cur = cur._next
            # cur指向了尾节点
            if cur.item == elem:
                pre._next = self._head



    def search(self,elem):
        if self.is_empty():
            return False
        cur = self._head
        index = 0
        while cur._next != self._head:
            if cur.item == elem:
                return index
            else:
                cur = cur._next
                index = index+1
        if cur.item == elem:
            return index
        else:
            return False

if __name__ == "__main__":
    ll = circularLinkList()
    # print(ll.length())
    ll.travel()
    print("--------首部添加----------")
    ll.first_add(2)
    ll.first_add(1)
    ll.first_add(99)
    ll.travel()
    print("--------尾部添加----------")
    ll.last_add(6)
    ll.travel()
    print("--------尾部添加----------")
    ll.last_add(5)
    ll.travel()
    print("--------移除元素----------")
    # ll.remove(5)
    # ll.travel()
    print("--------查找元素----------")
    print(ll.search(6))
   

3.栈
栈是一种特殊的线性表,仅能在线性表的一端操作,栈顶允许操作,栈底不允许操作。
栈的特性:后进先出

class Stack(object):
    def __init__(self):
        self.items = []
    def is_empty(self):
        # 判断栈是否为空
        return self.items == []
    def push(self,elem):
        # 添加元素到栈
        self.items.append(elem)
    def pop(self):
        # 弹出元素
        return self.items.pop()
    def peek(self):
        # 返回栈顶的元素
        return self.items[len(self.items)-1]
    def size(self):
        return len(self.items)
    def travel(self):
        for i in range(self.size()):
            print(self.items[i],end=" ")
if __name__ == '__main__':
    stack = Stack()
    print(stack.travel())
    print("======判断栈是否为空==========")
    print(stack.is_empty())
    print("======栈顶加入元素==========")
    stack.push(2)
    stack.push(3)
    stack.push(12)
    stack.push(4)
    print(stack.travel())
    print("栈的长度为:", stack.size())
    print("======判断栈是否为空==========")
    print(stack.is_empty())
    print("======弹出栈顶元素==========")
    print(stack.pop())
    print("======返回栈顶元素==========")
    print(stack.peek())
    print("栈的长度为:",stack.size())

4.队列
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。
特点:先进先出


image.png
class Queue(object):
    def __init__(self):
        self.items =[]
    def is_empty(self):
        return self.items == []
    def enqueue(self,elem):
        # 队列添加元素
        self.items.insert(0,elem)
    def dequeue(self):
        # 从队列头部删除一个元素
        return self.items.pop()
    def size(self):
        return len(self.items)
if __name__ == '__main__':
    queue = Queue()
    print("======判断队列是否为空==========")
    print(queue.is_empty())
    print("======队列加入元素==========")
    queue.enqueue(2)
    queue.enqueue("hello")
    queue.enqueue("end")

    print("队列的长度为:", queue.size())
    print("======判断队列是否为空==========")
    print(queue.is_empty())
    print("======返回队列头部元素==========")
    print(queue.dequeue())

    print("队列的长度为:",queue.size())

双端队列
特点:双端队列可以在队列任意一端入队和出队。

class DoubleQueue(object):
    """
    Deque() 创建一个空的双端队列
    add_front(item) 从队头加入一个item元素
    add_rear(item) 从队尾加入一个item元素
    remove_front() 从队头删除一个item元素
    remove_rear() 从队尾删除一个item元素
    is_empty() 判断双端队列是否为空
    size() 返回队列的大小
    """
    def __init__(self):
        self.items = []
    def is_empty(self):
        return self.items == []
    def add_front(self,elem):
        # 从队头加入一个元素
        self.items.insert(0,elem)
    def add_rear(self,elem):
        # 队尾加入一个元素
        self.items.append(elem)
    def remove_front(self):
        # 队头删除一个元素
        return self.items.pop(0)
    def remove_rear(self):
        # 队尾删除一个元素
        return self.items.pop()
    def size(self):
        return len(self.items)
    def travel(self):
        for i in range(self.size()):
            print(self.items[i],end=" ")
if __name__ == '__main__':
    deque = DoubleQueue()
    print("======判断双端队列是否为空==========")
    print(deque.is_empty())
    deque.add_front(1)
    deque.add_front(2)
    deque.add_rear(3)
    deque.add_rear(4)
    print("======双端队列的值为:==========")
    deque.travel()
    print("双端队列的长度为:",deque.size())
    print("======队头删除的元素==========")
    print(deque.remove_front())
    print("======队尾删除的元素==========")
    print(deque.remove_rear())
    print("双端队列的长度为:", deque.size())

4.树
二叉树
对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0=N2+1;
证明需要知道:二叉树(非空)的结点数等于边数+1(可以理解为,除了根节点,每一个节点对应一条边),可以设度数为0的节点为N0,度数为1的节点N1,度数为2的节点N2,有N0+N1+N2(节点数)= 1+N1+2N2(边数),度数为1的节点对对应一个边,度数为2的节点对应两个边。

  • 满二叉树:一棵深度为k且有2的k次方减1个结点的二叉树是满二叉树。除叶子结点外的所有结点均有两个子结点。节点数达到最大值。所有叶子结点必须在同一层上。


  • 完全二叉树:若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。



    二者的关系:满二叉树肯定是完全二叉树,完全二叉树不一定是满二叉树。

  • 平衡二叉树(AVL树):当且仅当任何节点的两棵子树的高度差不大于1的二叉树;
  • 排序二叉树(二叉查找树(英语:Binary Search Tree),也称二叉搜索树、有序二叉树);

树的前序遍历:中左右
中序遍历:左中右
后续遍历:左右中

5. 图 G=(V,E)

相关概念
图的组成:顶点V和边E
分类:有向图和无向图
顶点的度:对于无向图来说,一个顶点的度就是连接该顶点的边的数量,记做D(V);对于有向图来说,分为入度ID(V)(以该顶点为端点的入边数量)和出度OD(V)(以该顶点为端点的出边数量),D(V)=ID(V)+OD(V)
邻接顶点:对于无向图来说,就是一条边的两个顶点;对于有向图来说,两个顶点分别称为起始顶点(入边邻接顶点)和结束顶点(出边邻接顶点)
无向完全图:M个顶点的无向完全图总边数为M(M-1)/2
有向完全图:N个顶点的有向完全图,总边数为N(N-1)
有向无环图(DAG图):一个有向图从一个顶点出发经过若干条边无法回到该顶点。
网(即有权图)

图的存储结构
图的存储结构:邻接矩阵表示法、邻接表表示法
1⃣️邻接矩阵表示法:就是用两个数组表示图。缺点是n个顶点的图需要n*n个存储空间,当图为稀疏图时会造成空间浪费。
顶点表记录顶点信息,邻接矩阵表示顶点之间的关系。
有向图邻接矩阵中第i行表示以结点vi为尾的弧(即出度边[发出的弧]),第j列表示的是以结点vj为头的弧(即入度边),顶点的出度=第i行元素之和,顶点的入度=第j列元素之和

2⃣️邻接表表示法:(多重链表链式存储结构)


头结点:一维数组,其中有两个元素,第一个元素是顶点的数据元素本身,第二个元素是指针,指向边结点的地址。
表结点:第一个元素表示邻接的顶点是顶点表中的哪个元素,第二个指针指向下一个边。
对于一个顶点来说,有几个边就记录几个边结点
如果是网,可以在表结点中增加一个域表示权值。

特点:
邻接表不唯一;若无向图中有n个顶点e条边,则邻接表需要n个头结点和2e个表结点,适合存储稀疏图;有向图中有n个顶点e条边,则邻接表需要n个头结点和e个表结点
怎么看度?有几个表结点就是有几个度。

3.排序
冒泡排序(O(n^2))
思路:比较前后两个数的大小,从前到后比较到最后,这样第一次冒泡完最后一个数是整个里面最大的数,然后进行第二次冒泡...假设我们要排序的有m个数,那么就需要m-1次冒泡。

def BubbleSort(myList):
    length = len(myList)
    for i in range(length-1):
        #注意这个地方,因为还有一个j+1,所以j的取值是length-i-1
        for j in range(length-i-1):
            if myList[j]>myList[j+1]:
                myList[j],myList[j+1] = myList[j+1],myList[j]
        print(myList)
BubbleSort([49,38,65,97,76,13,27])

选择排序(O(n^2))
思路:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

注:选择排序每一趟只交换一个数!!所以在每一趟比较的时候记录下来最小的数的索引值,在未排序序列都遍历过一边之后,把最小的值和第一个为止的进行交换。

def Selection_sort(myList):
   length = len(myList)
   # 总共需要length-1次,因为第length次前面的都定了
   for i in range(length-1):
       min_index = i
       for j in range(i,length):
           if myList[j]

插入排序(O(n^2))
思路:未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

def insert_sort(myList):
    length = len(myList)
    for i in range(1,length):
        for j in range(i,0,-1):
            if myList[j]0 and myList[index-1]>temp:
                myList[index]=myList[index-1]
                index = index-1
            myList[index] = temp

insert_sort([49,38,65,97,76,13,27,49])

快速排序(O(nlogn))
思想:
算法-快速排序-python3实现
讲的很好的视频

def QuickSort(myList,start,end):
    i,j = start,end
    base = myList[i]
    while i=base:
            j = j-1
        myList[i]=myList[j]
        while i

快速排序2:

def quick_sort(self,lst):
        if not lst:
            return []
        pivot = lst[0]
        left = self.quick_sort([x for x in lst[1: ] if x < pivot])
        right = self.quick_sort([x for x in lst[1: ] if x >= pivot])
        
        return left + [pivot] + right

归并排序:
实质其实就是合并两个有序的列表

def merge_sort(collection):
    if len(collection)<=1:
        return collection
    mid = len(collection) // 2
    return merge(merge_sort(collection[:mid]), merge_sort(collection[mid:]))

def merge(left,right):
        result=[]
        while left and right:
            result.append(left.pop(0) if left[0]<=right[0] else right.pop(0))
        return result+left+right

print(merge_sort([2,-1,0,3,1,9]))

堆排序:
1⃣️Python实现:
堆排序的Python实现(附详细过程图和讲解)
主要的思想是先构建一个大根堆,然后让大根堆的顶点与堆右下角的元素交换,再构建成大根堆。
堆,其实就是一个完全二叉树,特点是根节点的值最大(大根堆)或者最小(小根堆)
2⃣️Python调用heaq库 (参考这篇文章Python标准库模块之heapq)

# 使用heaq库构建堆
import heapq
nums = [2, 3, 5, 1, 54, 23, 132]
heap = []
for num in nums:
    heapq.heappush(heap, num)
#heapq.heappop() 函数弹出堆中最小值
print([heapq.heappop(heap) for _ in range(len(nums))])
#获取堆中的最大或最小的范围值
print(heapq.nlargest(3, nums))
print(heapq.nsmallest(3, nums))

4.常见算法
穷举法
贪心法:典型例子是最大连续子数组和(不太确定下面这个是不是用贪心法做的)

class Solution:
    def FindGreatestSumOfSubArray(self, array):
        # write code here
        if not array:
            return 0
        
        cur_sum = 0
        max_sum = array[0]
        
        for i in range(len(array)):
            if cur_sum <= 0:
                cur_sum = array[i]
            else:
                cur_sum += array[i]
                
            if cur_sum > max_sum:
                max_sum = cur_sum
                
        return max_sum

分治法 :典型例子有快排
回溯法:例子有那个左上到右下的棋盘
动态规划 :

你可能感兴趣的:(数据结构与算法Python版(ing))