目录

数据结构与算法分析

目录

栈(stack)是限制插入和删除只能在一个位置上进行的表,该位置是表的末端,叫作栈的顶(top).栈也叫做LIFO(后进先出)表.

1
2
3
4
5
6
7
int IsEmpty(Stack S);
Stack CreateStack(void);
void DisposeStack(Stack S);
void MakeEmpty(Stack S);
void Push(ElementType X, Stack S);
ElementType Top(Stack S);
void Pop(Stack S);

由于栈是一个表,因此任何实现表的方式都能实现栈.一般有两种实现方法,一种使用链式结构,另一种使用数组.

使用数组来实现栈,通过在表顶端???来实现Push,通过删除表顶端元素实现Pop.Top操作只是考察表顶端元素并返回它的值.

数组实现的特点

  • 需要提前声明数组大小
  • 连续的内存空间
  • 在表头插入和删除操作效率低
  • 支持随机访问元素

SqStack.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#ifndef SQSTACK_H_
#define SQSTACK_H_

// Type Definition
struct StackRecord;
typedef int ElementType;
typedef struct StackRecord *SqStack;

// Function Lists
int IsEmpty(SqStack S);
int IsFull(SqStack S);
SqStack CreateStack(int MaxElements);
void DisposeStack(SqStack S);
void MakeEmpty(SqStack S);
void Push(ElementType X, SqStack S);
ElementType Top(SqStack S);
void Pop(SqStack S);
ElementType TopAndPop(SqStack S);

#endif  /* SQSTACK_H_ */

SqStack.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include "SqStack.h"
#include <stdlib.h>  // for malloc
#include "FatalError.h"

/* Stack implementation is a dynamically allocated array */
#define EmptyTOS     (-1)
#define MinStackSize (5)

struct StackRecord {
    int Capacity;
    int TopOfStack;
    ElementType *Array;
};

int IsEmpty(SqStack S) {
    return S->TopOfStack == EmptyTOS;
}

int IsFull(SqStack S) {
    return S->TopOfStack == S->Capacity - 1;
}

SqStack CreateStack(int MaxElements) {
    SqStack S;

    if (MaxElements < MinStackSize) {
        Error("Stack size is too small");
    }

    S = (SqStack)malloc(sizeof(struct StackRecord));
    if (S == NULL) {
        FatalError("Out of space!!!");
    }

    S->Array = (ElementType *)malloc(sizeof(ElementType) * MaxElements);
    if (S->Array == NULL) {
        FatalError("Out of space!!!");
    }

    S->Capacity = MaxElements;
    MakeEmpty(S);

    return S;
}

void DisposeStack(SqStack S) {
    if (S != NULL) {
        free(S->Array);
        free(S);
    }
}

void MakeEmpty(SqStack S) {
    S->TopOfStack = EmptyTOS;
}

void Push(ElementType X, SqStack S) {
    if (IsFull(S)) {
        Error("Full stack");
    } else {
        S->Array[++S->TopOfStack] = X;
    }
}

ElementType Top(SqStack S) {
    if (!IsEmpty(S)) {
        return S->Array[S->TopOfStack];
    } else {
        Error("Empty stack");
        return 0;  /* Return value used to avoid warning */
    }
}

void Pop(SqStack S) {
    if (IsEmpty(S)) {
        Error("Empty stack");
    } else {
        --S->TopOfStack;
    }
}

ElementType TopAndPop(SqStack S) {
    if (!IsEmpty(S)) {
        return S->Array[S->TopOfStack--];
    } else {
        Error("Empty stack");
        return 0;  /* Return value used to avoid warning */
    }
}

使用单链表来实现栈,通过在表顶端插入来实现Push,通过删除表顶端元素实现Pop.Top操作只是考查表顶端元素并返回它的值.

链表实现的特点

  • 不需要提前声明分配空间
  • 分散的内存空间
  • 在表头插入和删除操作效率高
  • 不支持随机访问元素

LinkedStack.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#ifndef LINKEDSTACK_H_
#define LINKEDSTACK_H_

// Type Definition
struct Node;
typedef int ElementType;
typedef struct Node *PtrToNode;
typedef PtrToNode LinkedStack;

// Function Lists
int IsEmpty(LinkedStack S);
LinkedStack CreateStack(void);
void DisposeStack(LinkedStack S);
void MakeEmpty(LinkedStack S);
void Push(ElementType X, LinkedStack S);
ElementType Top(LinkedStack S);
void Pop(LinkedStack S);
ElementType TopAndPop(LinkedStack S);

#endif  /* LINKEDSTACK_H_ */

LinkedStack.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include "LinkedStack.h"
#include <stdlib.h>  // for malloc
#include "FatalError.h"

/* Stack implementation is a linked list with a header */
struct Node {
    ElementType Element;
    PtrToNode   Next;
};

int IsEmpty(LinkedStack S) {
    return S->Next == NULL;
}

LinkedStack CreateStack(void) {
    LinkedStack S;

    S = (LinkedStack)malloc(sizeof(struct Node));
    if (S == NULL) {
        FatalError("Out of space!!!");
    }

    S->Next = NULL;
    MakeEmpty(S);

    return S;
}

void DisposeStack(LinkedStack S) {
    MakeEmpty(S);
    free(S);
}

void MakeEmpty(LinkedStack S) {
    if (S == NULL) {
        Error("Must use CreateStack first");
    } else {
        while (!IsEmpty(S)) {
            Pop(S);
        }
    }
}

void Push(ElementType X, LinkedStack S) {
    PtrToNode TmpCell;

    TmpCell = (PtrToNode)malloc(sizeof(struct Node));
    if (TmpCell == NULL) {
        FatalError("Out of space!!!");
    } else {
        TmpCell->Element = X;
        TmpCell->Next = S->Next;
        S->Next = TmpCell;
    }
}

ElementType Top(LinkedStack S) {
    if (!IsEmpty(S)) {
        return S->Next->Element;
    } else {
        Error("Empty stack");
        return 0;  /* Return value used to avoid warning */
    }
}

void Pop(LinkedStack S) {
    PtrToNode FirstCell;

    if (IsEmpty(S)) {
        Error("Empty stack");
    } else {
        FirstCell = S->Next;
        S->Next = S->Next->Next;
        free(FirstCell);
    }
}

ElementType TopAndPop(LinkedStack S) {
    PtrToNode FirstCell;
    ElementType X = 0;

    if (IsEmpty(S)) {
        Error("Empty stack");
        return 0;  /* Return value used to avoid warning */
    } else {
        X = S->Next->Element;  /* Save the value of the first element */

        FirstCell = S->Next;
        S->Next = S->Next->Next;
        free(FirstCell);

        return X;
    }
}

平衡符号

后缀表达式

函数调用



查找算法

插入算法

删除算法

查找算法

插入算法


基本思想

算法代码

基本的时间复杂度分析

基本思想

算法代码

基本的时间复杂度分析

基本思想

算法代码

基本的时间复杂度分析

基本思想

算法代码

基本的时间复杂度分析

基本思想

算法代码

基本的时间复杂度分析

基本思想

算法代码

基本的时间复杂度分析


一个是由有限非空顶点集边集组成.

每一条边都是一副点对.如果边有方向,那么就是有向边,表示为<v, w>.如果边没有方向,那么就是无向边,表示为(v, w).

如果点对是有序的,那么图就是有向的.如果点对是无序的,那么图就是无向的.

无向图中,顶点v的是指和顶点v相关联的边的数目.

有向图中,以顶点v为弧头的弧的数目称为顶点v的入度,以顶点v为弧尾的弧的数目称为顶点v的出度

路径是有顶点序列组成,路径的长为该路径上的边数.

序列中顶点不重复出现的路径称为简单路径

若一条路径中第一个顶点和最后一个顶点相同,则这条路径是一条回路

如果图含有一条从一个顶点到它自身的边(v, w),那么路径vw有时也称作为(loop).我们一般讨论的都是无环图!

如果在一个无向图中从每一个顶点到每个其他顶点都存在一条路径,则称该无向图是连通的.具有这样性质的有向图称为是强连通的.

无向图中的极大连通子图为其连通分量.有向图中的极大强连通子图称为有向图的强连通分量

每条边都可以附带一个数,这种与边相关的数称为,权可以表示从一个顶点到另一个顶点的距离或者花费的代价。边上带权的图称为带权图

完全图是其每一对顶点间都存在一条边的图.

表示图的一种简单的方法是使用一个二维数组,称为邻接矩阵表示法.

表示图的另一种方法是使用数组与链表相结合的存储方式,成为邻接表表示法.

从图中某一顶点出发访问图中其余顶点,且每个顶点只访问一次,这一过程成为图的遍历.

图的遍历分为广度优先遍历和深度优先遍历.

广度优先搜索算法(BFS)类似于树的层次遍历

基本思想:从图中某顶点v出发,在访问了v之后依次访问v的各个未曾访问过的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点,并使得“先被访问的顶点的邻接点先于后被访问的顶点的邻接点被访问,直至图中所有已被访问的顶点的邻接点都被访问到。如果此时图中尚有顶点未被访问,则需要另选一个未曾被访问过的顶点作为新的起始点,重复上述过程,直至图中所有顶点都被访问到为止。

图的深度优先搜索遍历(DFS)类似于二叉树的先序遍历。

基本思路:假设初始状态是图中所有顶点均未被访问,则从某个顶点v出发,首先访问该顶点,然后依次从它的各个未被访问的邻接点出发深度优先搜索遍历图,直至图中所有和v有路径相通的顶点都被访问到。 若此时尚有其他顶点未被访问到,则另选一个未被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。

一个无向图的最小生成树就是由该图的那些连接无向图的所有顶点的边构成的树,且其总价值最低.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public void floyd(int[][] path, int[][] dist) {
    // 初始化
    for (int i = 0; i < mVexs.length; i++) {
        for (int j = 0; j < mVexs.length; j++) {
            dist[i][j] = mMatrix[i][j];    // "顶点i"到"顶点j"的路径长度为"i到j的权值"。
            path[i][j] = j;                // "顶点i"到"顶点j"的最短路径是经过顶点j。
        }
    }

    // 计算最短路径
    for (int k = 0; k < mVexs.length; k++) {
        for (int i = 0; i < mVexs.length; i++) {
            for (int j = 0; j < mVexs.length; j++) {

                // 如果经过下标为k顶点路径比原两点间路径更短,则更新dist[i][j]和path[i][j]
                int tmp = (dist[i][k]==INF || dist[k][j]==INF) ? INF : (dist[i][k] + dist[k][j]);
                if (dist[i][j] > tmp) {
                    // "i到j最短路径"对应的值设,为更小的一个(即经过k)
                    dist[i][j] = tmp;
                    // "i到j最短路径"对应的路径,经过k
                    path[i][j] = path[i][k];
                }
            }
        }
    }
}

递归解法:

  • 如果二叉树为空,返回0
  • 如果二叉树是叶子节点,返回1
  • 如果二叉树不是叶子节点,二叉树的叶子节点数 = 左子树叶子节点数 + 右子树叶子节点数
1
2
3
4
5
6
7
8
9
public static int getNodeNumLeafRec(TreeNode root) {
    if (root == null) {
        return 0;
    }
    if (root.left == null && root.right == null) {
        return 1;
    }
    return getNodeNumLeafRec(root.left) + getNodeNumLeafRec(root.right);
}

非递归解法:基于层次遍历进行求解,利用Queue进行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static int getNodeNumLeaf(TreeNode root){
    if (root == null) {
        return 0;
    }

    int leaf = 0; // 叶子节点个数
    Queue<TreeNode> queue = new LinkedList<>();
    queue.add(root);
    while (!queue.isEmpty()) {
        TreeNode temp = queue.poll();
        if (temp.left == null && temp.right == null) { // 叶子节点
            leaf++;
        }

        if (temp.left != null) {
            queue.add(temp.left);
        }

        if (temp.right != null) {
            queue.add(temp.right);
        }
    }

    return leaf;

用PriorityQueue默认是自然顺序排序,要选择最大的k个数,构造小顶堆,每次取数组中剩余数与堆顶的元素进行比较,如果新数比堆顶元素大,则删除堆顶元素,并添加这个新数到堆中。

Java中的PriorityQueue来实现堆,用PriorityQueue的实现的代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class findTopK {
    // 找出前k个最大数,采用小顶堆实现
    public static int[] findKMax(int[] nums, int k) {
        PriorityQueue<Integer> pq = new PriorityQueue<>(k);  // 队列默认自然顺序排列,小顶堆,不必重写compare

        for (int num : nums) {
            if (pq.size() < k) {
                pq.offer(num);
            } else if (pq.peek() < num) {  // 如果堆顶元素 < 新数,则删除堆顶,加入新数入堆
                pq.poll();
                pq.offer(num);
            }
        }

        int[] result = new int[k];
        for (int i = 0; i < k && !pq.isEmpty(); i++) {
            result[i] = pq.poll();
        }
        return result;
    }

    public static void main(String[] args) {
        int[] arr = new int[]{1, 6, 2, 3, 5, 4, 8, 7, 9};
        System.out.println(Arrays.toString(findKMax(arr,5)));
    }
}
// 输出:[5, 6, 7, 8, 9]

要选择最小的K个数使用大顶堆,每次取数组中剩余数与堆顶的元素进行比较,如果新数比堆顶元素小,则删除堆顶元素,并添加这个新数到堆中。

Java中的PriorityQueue来实现堆,用PriorityQueue的实现的代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class findTopK {
    // 要找前k个最小数,则构建大顶堆,要重写compare方法
    public static int[] findKMin(int[] nums, int k) {
        PriorityQueue<Integer> pq = new PriorityQueue<>(k, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2-o1;
            }
        });

        for (int num : nums) {
            if (pq.size() < k) {
                pq.offer(num);
            } else if (pq.peek() > num) {//如果堆顶元素 > 新数,则删除堆顶,加入新数入堆
                pq.poll();
                pq.offer(num);
            }
        }

        int[] result = new int[k];
        for (int i = 0; i < k&&!pq.isEmpty(); i++) {
            result[i] = pq.poll();
        }

        return result;
    }

    public static void main(String[] args) {
        int[] arr = new int[]{1, 6, 2, 3, 5, 4, 8, 7, 9};
        System.out.println(Arrays.toString(findKMin( arr,5)));
    }

}
// 输出:[5, 4, 3, 2, 1]