The algorithm of python Development & data structure

Keywords: Python Lambda Programming

The algorithm of python Development & data structure (4)

data structure

1. Circular queue

  • Circular queue: when the pointer = = Maxsize-1, another advance position will automatically reach 0.
  • Team head pointer forward 1: front = (front + 1)% maxsize
  • End of team pointer forward 1: rear = (rear + 1)% maxsize
  • Air condition: rear == front
  • Team full condition: (rear + 1)% maxsize = = front
class Queue:
    def __init__(self,size=100):
        self.queue = [0 for _ in range(size)]
        self.size = size
        self.rear = 0   # End of line pointer
        self.front = 0   # Team leader pointer

    def push(self, element):
        if not self.is_filled():
            self.rear = (self.rear + 1)%self.size
            self.queue[self.rear] = element
        else:
            raise IndexError("Queue is filled")

    def pop(self):
        if not self.is_empty():
            self.front = (self.front+1) % self.size
            return self.queue[self.front]
        else:
            raise IndexError("Queue is empty")

    # Judge team empty
    def is_empty(self):
        return self.rear == self.front

    # Judge team full
    def is_filled(self):
        return (self.rear+1) % self.size == self.front

2. Python queue built-in module

  • Usage: from collections import deque

  • Create queue: queue = deque()

  • Team entry: append()

  • Out of line: poppleft ()

  • Two way queue: appendleft()

  • Two way queue tail out: pop()

3. List

3.1 creating a linked list

Head insertion:

(1) node.next =Head, first point the next of the new node to head.

(2) head = node, and then point the head to the newly added node.

Tail insertion method:

(1) tail.next =Node, first point the next of tail node to the newly added node.

(2) tail = node, and then point the tail to the newly added node.

3.2 insertion of linked list nodes

(1)p.next = curNode.next , first point the next of the node to be inserted to the next of the curnode.

(2) curNode.next =p, and then point the next of curnode to the newly added node p.

3.3 deletion of linked list nodes

(1)p = curNode.next

​ curNode.next =p.next, first point the next of curnode to the next of p.

(2) del p, then delete p.

4. Double linked list

4.1 insertion of double linked list nodes

(1)p.next = curNode.next , first point the next of P to the next of curnode.

(2) curNode.next.prior =p, and then point the prior of next of curnode to p.

(3) p.prior = curNode, and then point p's prior to curNode.

(4) curNode.next =p, and finally point the next of curnode to p.

4.2 deletion of bidirectional linked list nodes

(1)p = curNode.next

​ curNode.next =p.next, first point the next of curnode to the next of p.

(2)p. next.prior =curNode, and then point the prior of the next of P to curNode.

del p, delete p at last.

4.3 chain list and sequence list

  • The operation of insertion and deletion of linked list is faster than that of sequential list.
  • The memory of linked list can be allocated more flexibly.
  • The data structure of linked list is very enlightening to the structure of tree and graph.

5. AVL tree - rotation

  1. Left hand code:
def rotate_left(self,p,c):
    s2 = c.lchild
    p.child = s2
    if s2:   # To find the parent reversely, you need to judge whether it is empty or not
        s2.parent = p

    c.lchild = p
    p.parent = c

    p.bf = 0
    c.bf = 0
    return c

  1. Right hand code:
def rotate_right(self,p,c):
    s2 = c.rchild
    p.lchild = s2
    if s2:
        s2.parent = p

    c.rchild = p
    p.parent = c

    p.bf = 0
    c.bf = 0
    return c

  1. Right left Code:

    def rotate_right_left(self,p,c):
        g = c.lchild
        s3 = g.rchild
        c.lchild = s3
        if s3:
            s3.parent = c
        g.rchild = c
        c.parent = g
    
        s2 = g.lchild
        p.rchild = s2
        if s2:
            s2.parent = p
        g.lchild = p
        p.parent = g
    
        # Update bf
        if g.bf > 0:   # Insert at s3
            p.bf = -1
            c.bf = 0
        elif g.bf < 0:   # Insert at s2
            p.bf = 0
            c.bf = 1
        else:   # S1, S2, S3 and S4 are all empty. The inserted one is actually g
            p.bf = 0
            c.bf = 0
        g.bf = 0
        return g
    

  1. Left right code:

    def rotate_left_right(self,p,c):
        g = c.rchild
        s2 = g.lchild
        c.rchild = s2
        if s2:
            s2.parent = c
        g.lchild = c
        c.parent = g
    
        s3 = g.rchild
        p.lchild = s3
        if s3:
            s3.parent = p
        g.rchild = p
        p.parent = g
    
        # Update bf
        if g.bf < 0:   # Insert at s2
            p.bf = 1
            c.bf = 0
        elif g.bf > 0:   # Insert at s3
            p.bf = 0
            c.bf = -1
        else:   # S1, S2, S3 and S4 are all empty. The inserted one is actually g
            p.bf = 0
            c.bf = 0
        g.bf = 0
        return g
    

Extended application of binary search tree - B tree:

algorithm

1. Greedy algorithm

1.1 change problem

Suppose that the shopkeeper needs to change n yuan, and the denomination of the coin is: 100 yuan, 50 yuan, 20 yuan, 5 yuan, 1 yuan. How to change to minimize the number of coins needed?

Code implementation:

# Change question:
t = [100,50,20,5,1]

def change(t,n):
    m = [0 for _ in range(len(t))]
    for i, money in enumerate(t):
        m[i] = n // money
        n = n % money
    return m,n

print(change(t,376))

1.2 fractional knapsack problem


Code implementation:

# Fractional knapsack problem:
goods = [(60,10),(100,20),(120,30)]  # Yuan Zu of each commodity (price, weight)
goods.sort(key=lambda x: x[0]/x[1], reverse=True)   # Calculate unit value of goods and sort them from large to small

def fractional_backpack(goods,w):
    m = [0 for _ in range(len(goods))]
    total_v = 0
    for i, (price,weight) in enumerate(goods):
        if w >= weight:
            m[i] = 1
            total_v += price
            w -= weight
        else:
            m[i] = w / weight
            total_v += m[i] * price
            w = 0
            break
    return total_v, m

print(fractional_backpack(goods,50))

1.3 digital splicing

Code implementation:

# Digital splicing:
from functools import cmp_to_key

li = [32,94,128,1286,6,71]

def xy_cmp(x,y):
    if x+y < y+x:
        return 1
    elif x+y > y+x:
        return -1
    else:
        return 0

def number_join(li):
    li = list(map(str,li))
    li.sort(key=cmp_to_key(xy_cmp))
    return "".join(li)

print(number_join(li))

1.4 activity selection

Code implementation:

# Activity selection questions:
activities = [(1,4),(3,5),(0,6),(5,7),(3,9),(5,9),(6,10),(8,11),(8,12),(2,14),(12,16)]
# Ensure activities are ordered by end time
activities.sort(key=lambda x: x[1])

def activity_selection(a):
    res = [a[0]]
    for i in range(1, len(a)):
        if a[i][0] >= res[-1][1]:  # The start time of the current activity is greater than or equal to the end time of the last selected activity
            # No conflict
            res.append(a[i])
    return res

print(activity_selection(activities))

2. Dynamic planning

The key of dynamic programming is to find recurrence!!!

2.1 dynamic planning from Fibonacci series

Code implementation:

# Recursive Fibonacci series: repeated calculation with subproblems, affecting efficiency
def fibnacci(n):
    if n == 1 or n == 2:
        return 1
    else:
        return fibnacci(n-1) + fibnacci(n-2)

# Non recursive Fibonacci series:
# The idea of dynamic programming (DP) = recursive + repetitive subproblem
def fibnacci_no_recurision(n):
    f = [0,1,1]
    if n > 2:
        for i in range(n-2):
            num = f[-1] + f[-2]
            f.append(num)
    return f[n]

print(fibnacci_no_recurision(100))

2.2 steel bar cutting



Code implementation:

p = [0,1,5,8,9,10,17,17,20,24,30]

# -----------Top down-------------
# Recursive version 1 steel bar cutting: repeat two sub problems
def cut_rod_recurision_1(p,n):
    if n == 0:
        return 0
    else:
        res = p[0]
        for i in range(1,n):
            res = max(res,cut_rod_recurision_1(p,i) + cut_rod_recurision_1(p, n-i))
        return res


# Recursive version 2 steel bar cutting: repeating a subproblem
def cut_rod_recurision_2(p ,n):
    if n == 0:
        return 0
    else:
        res = 0
        for i in range(1, n+1):
            res = max(res, p[i]+cut_rod_recurision_2(p,n-i))
        return res


# -------------Bottom up--------------
# Dynamic planning version
def cut_rod_dp(p,n):
    r = [0]
    for i in range(1,n+1):
        res = 0
        for j in range(1,i+1):
            res = max(res,p[j]+r[i-j])
        r.append(res)
    return r[n]

Time complexity

The top-down time complexity is: O(2^n)

The bottom-up time complexity is: O(n^2)

Code implementation:

# ---------Output cutting scheme (where to cut)-----------
def cut_rod_extend(p,n):
    r = [0]
    s = [0]
    for i in range(1,n+1):
        res_r = 0   # Maximum price
        res_s = 0   # The length of the left non cutting part of the scheme corresponding to the maximum price
        for j in range(1,i+1):
            if p[j] + r[i-j] > res_r:
                res_r = p[j] + r[i-j]
                res_s = j
        r.append(res_r)
        s.append(res_s)
    return r[n],s

def cut_rod_solution(p,n):
    r,s = cut_rod_extend(p,n)
    ans = []
    while n > 0:
        ans.append(s[n])
        n -= s[n]
    return ans

2.3 the longest common subsequence problem




Code implementation:

# ---------Finding the length of the longest common subsequence------------
def lcs_length(x,y):
    m = len(x)
    n = len(y)
    c = [[0 for _ in range(n+1)] for _ in range(m+1)]  # List generation method, generating a (m+1)*(n+1) two-dimensional list
    for i in range(1,m+1):
        for j in range(1,n+1):
            if x[i-1] == y[j-1]:  # When the characters in position i j match, they come from the top left + 1
                c[i][j] = c[i-1][j-1] + 1
            else:
                c[i][j] = max(c[i-1][j],c[i][j-1])
   

# ----------What is the longest subsequence-------------
def lcs(x,y):
    m = len(x)
    n = len(y)
    c = [[0 for _ in range(n+1)] for _ in range(m+1)]
    b = [[0 for _ in range(n+1)] for _ in range(m+1)] # 1: Top left 2: Top 3: left
    for i in range(1,m+1):
        for j in range(1,n+1):
            if x[i-1] == y[j-1]:   # When the characters in position i j match, they come from the top left + 1
                c[i][j] = c[i-1][j-1] + 1
                b[i][j] = 1
            elif c[i-1][j] > c[i][j-1]:   # From above
                c[i][j] = c[i-1][j]
                b[i][j] = 2
            else:
                c[i][j] = c[i][j-1]
                b[i][j] = 3
    return c[m][n], b

# Backtracking: finding the longest subsequence
def lcs_trackback(x,y):
    c,b = lcs(x,y)
    i = len(x)
    j = len(y)
    res = []
    while i > 0 and j > 0:
        if b[i][j] == 1:   # From top left = > match
            res.append(x[i-1])
            i -= 1
            j -= 1
        elif b[i][j] == 2:   # From above = > mismatch
            i -= 1
        else:   # ==3, from left = > mismatch
            j -= 1
    return "".join(reversed(res))

2.4 Euclidean algorithm

Code implementation:

# Greatest Common Divisor(GCD)
def gcd(a,b):
    if b == 0:
        return a
    else:
        return gcd(b, a % b)

# Finding the greatest common divisor of non recursive version
def gcd2(a,b):
    while b > 0:
        r = a % b
        a = b
        b = r
    return a

Application of Euclidean algorithm: to realize fractional computation

Code implementation:

class Fraction:
    def __init__(self,a,b):
        self.a = a
        self.b = b
        x = self.gcd(a,b)
        self.a /= x
        self.b /= x

    def gcd(self,a, b):
        while b > 0:
            r = a % b
            a = b
            b = r
        return a

    # Least Common Multiple
    def lcm(self,a,b):
        x = self.gcd(a,b)
        return a * b / x

    def __add__(self, other):  # self is a number, the other is another number, find the sum of the two numbers
        a = self.a   # Molecule of the first number
        b = self.b   # First! [insert picture description here]( https://img-blog.csdnimg.cn/2020050617595319.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0RyX0JpZ0pvZQ==,size_16,color_FFFFFF,t_70)
//Denominator of number
        c = other.a   # Molecules of the second number
        d = other.b   # Denominator of the second number
        print(a,b,c,d)
        # numerator, denominator
        denominator = self.lcm(b,d)
        numerator = a * denominator / b + c * denominator / d
        return Fraction(numerator,denominator)

    def __str__(self):
        return "%d/%d" % (self.a, self.b)

2.5 RSA encryption algorithm Introduction




The difficulty of RSA encryption is that it's easy to select two prime numbers for multiplication, but it's difficult to use the number obtained by multiplication to deduce which two prime numbers are multiplied by each other.

==================================================================

See week17 for the code involved

Posted by sm on Thu, 25 Jun 2020 02:27:58 -0700