软件设计师 第三章 数据结构(上)

数据结构

  • 1.时间复杂度
    • 1.2渐进符号
    • 1.3递归的时间复杂度
  • 2.线性结构(逻辑)
    • 2.1线性表的顺序存储
    • 2.2线性表的链式存储
    • 2.3循环链表
    • 2.4双链表
  • 3.栈
    • 3.1栈的顺序存储
    • 3.2栈的链式存储(链栈)
  • 4.队列
    • 4.1队列的顺序存储
    • 4.2队列的链式存储
  • 5.串
    • 5.1串的匹配模式
  • 6.数组
  • 7.矩阵
    • 7.1对称矩阵 (aij = aji)
    • 7.2三对角矩阵
    • 7.3稀疏矩阵
  • 8.树(非线性)
    • 8.1性质
    • 8.2满二叉树与完全二叉树
    • 8.3二叉树的存储
    • 8.4二叉树的遍历
    • 8.5平衡二叉树
    • 8.6二叉排序树(二叉查找树)
    • 8.7最优二叉树(哈夫曼树)
    • 8.8线索二叉树
  • 9.图(线性结构)
    • 9.1图的存储结构
    • 9.2图的遍历
    • 9.3拓扑排序

1.时间复杂度

保留最高阶项,系数化为1

O(1)<O(log₂n)<O(n)<O(2ⁿ)<O(n!)<O(nⁿ)

1.2渐进符号

O:渐进上界 >=
Ω:渐进下界 <=
Θ:渐进紧致界 =

1.3递归的时间复杂度

1.每次递归的时间复杂度不变的情况:
递归的次数 * 每次递归的时间复杂度

2.递归式主方法:
T(n) = aT(n/b) + f(n)
在这里插入图片描述
在这里插入图片描述
一个算法比另一个快,时间复杂度小

2.线性结构(逻辑)

通常采用顺序存储,链式存储

基本操作:插入,删除,查找

1.定义:线性表是n个元素的有限序列

2.非空线性表特点:
除第一个元素外,序列中的每个元素均只有一个直接前驱
除最后一个元素外,序列中的每个元素均只有一个直接后继

2.1线性表的顺序存储

一组地址连续的存储单元,依次存储线性表中的数据元素

优点:可以随机存取表中的数据元素
缺点:插入和删除需要移动元素

插入一个元素(共有n+1个插入位置)的期望值:n/2
删除一个元素(共有n个可删除元素)的期望值:
(n-1)/2

线性表顺序存储

class SequenceList {
  int N = 10; // 容量  
  int[] arr;  
  int n; // 表长(表中元素的个数) 
   
  void init() {    
    arr = new int[N];    
    for (int i = 0; i < N / 2; i++) { 
      //给一半的元素      
      arr[i] = i + 1;    
    }    
    n = N / 2;    
    for (int i = 0; i < n; i++) { 
      //打印输出所赋值的元素      
      System.out.print(arr[i] + " ");    
    }  
  }
}

插入
时间复杂度:最好O(1),最坏和平均O(n)

void insert(int k, int x) { 
   // k位置,x要插入的数字
  if (k < 1 || k > n + 1) {//越界      
     return ;    
  }      
     for (int i = n; i >= k; i--) {
        //从最后一个数开始往后移        
        arr[i] = arr[i - 1];      
     }      
     arr[k - 1] = x;//数组从0开始,所以k-1      
     n++;//插入之后表长要+1      
     for (int i = 0; i < n; i++) { 
        // 打印输出        
        System.out.print(arr[i] + " ");      
     }      
     System.out.println();    
}

删除
时间复杂度:最好O(1),最坏和平均O(n)

void delete(int k) { // k要删除的位置
    if (k < 1 || k > n) { // 越界      
       return false;    
    }     
       for (int i = k; i < n; i++) { 
          // 从k开始往前移        
          arr[i - 1] = arr[i];      
        }      
        n--; // 插入之后表长要-1      
        for (int i = 0; i < n; i++) { 
           // 打印输出        
           System.out.print(arr[i] + " ");      
         }      
         System.out.println();   
}

查找(特别快)
时间复杂度:O(1)

int getElement(int k) {
    if (k < 1 || k > n) {      
        return -1;    
    }    
    return arr[k - 1];  
 }

2.2线性表的链式存储

数据域和指针域

头指针:指向链表的第一个结点
头结点:链表的第0个结点

插入,删除,查找的时间复杂度:
最好O(1),最坏和平均O(n)

class Node {
  int data;  
  Node next; // 省略了new Node();相当于指针  
  
  public Node(int data) {    
    this.data = data;  
  }  
  
  public Node() {
  }
}

不带头结点

class LinkList {
  Node list;  

  void init() {    
  // 调用有参构造方法    
    Node n1 = new Node(1);    
    Node n2 = new Node(2);    
    Node n3 = new Node(3);    

    list = n1; // list指向n1    
    n1.next = n2; // 把n2赋值给n1的指针    
    n2.next = n3;  
  }  
  void printList() {    
    Node p = list; // 可以构成循环,没有的话只会执行一次    

    while (p != null) {      
      System.out.print(p.data + " ");      
      p = p.next; // p的下一个结点地址赋给p    
    }    
    System.out.println();  
  }
}

带头结点

class HeadLinkList {
  Node head;  
  void init() {    
    head = new Node(); // 调用无参构造方法,因为头结点不存储数据 
       
    Node n1 = new Node(3);    
    Node n2 = new Node(2);    
    Node n3 = new Node(1);    
    
    head.next = n1; // 头结点的下一个结点指向n1    
    n1.next = n2; // 把n2赋值给n1的指针    
    n2.next = n3;  
  }  
  
  void printList() {    
    Node p = head.next; // 头结点不存储数据,所以要指向头结点的下一个结点    
    
    while (p != null) {      
      System.out.print(p.data + " ");      
      p = p.next; // p的下一个结点地址赋给p    
      }    
    System.out.println();  
  }
}

带头结点插入

boolean insert(int k, Node node) {//k是位置
    if (k < 1) {      
       return false;    
    }    
    int i = 0;//i从0开始是头结点    
    Node p = head; // 指针指向头结点   
     
    while (i < k - 1 && p != null) {
       //i,p往后移      
       i++;      
       p = p.next;    
    }    
    if (p == null) {      
       return false;    
    }    
    
    node.next = p.next;    
    p.next = node;    
    return true;  
}


node.next = p.next;
在这里插入图片描述
p.next = node;
不带头结点的插入

boolean insert(int k, Node node) { // k是位置
    if (k < 1) {      
       return false;    
    }    
    
    if (k == 1) {      
       node.next = list;      
       list = node;      
    return true;    
    }    
    
    int i = 1;    
    Node p = list;    
    while (i < k - 1 && p != null) { 
       // i,p往后移      
       i++;      
       p = p.next;    
    }    
    
    if (p == null) {      
       return false;    
    }    
    
    node.next = p.next;    
    p.next = node;    
    return true;  
}

带头结点删除

boolean delete(int k) {
    if (k < 1 || k > head.data) {      
      return false;    
    }    
    int i = 0;    
    Node p = head;    
    while (i < k - 1) {      
      i++;      
      p = p.next;//p往后移    
    }    
    Node s = p.next;    
    p.next = s.next;
    //p指向下一个结点的下一个结点  
    head.data--;  
    return true;  
    }

不带头结点删除

boolean delete(int k) {
    if (k < 1 || k > length) {      
      return false;    
    }        
    
    if(k == 1){        
      list = list.next;        
      return true;    
    }    
    int i = 1;    
    Node p = list;    
    while (i < k - 1) {      
      i++;      
      p = p.next; // p往后移    
    }    
    Node s = p.next;    
    p.next = s.next; 
    // p指向下一个结点的下一个结点    
    length--;    
    return true;  
    }

查找

Node getData(int k) {
  if (k < 1 || k > head.data) {      
    return null;    
  }    
  int i = 1; // 找第k个所以从1开始    
  Node p = head.next;    
  while (i < k) {      
    i++;      
    p = p.next;    
  }    
  return p;  
}

2.3循环链表

插入尾指针:直接插
删除:要循环得到前一个结点

2.4双链表

插入

Node.next = p.next;
if(p.next != null){
  p.next.prev = Node;
}
p.next = Node;
Node.next.prev = p;

在这里插入图片描述
删除

Node s = p.next;
p.next = s.next;
if(s.next != null){
  s.next.prev = p;
}

在这里插入图片描述

3.栈

先进后出,后进先出

3.1栈的顺序存储

初始化空栈

int[] a = new int[5];
top = 0;

判栈空

if(top == 0) return true;
return false;

入栈

a[top] = x;
top++;

出栈

int pop(){
  if(isEmpty()) return -1;
  top--;
  return a[top];
}

读栈顶元素

int Top(){
  if(isEmpty()) return -1;
  return a[top -1];
}

3.2栈的链式存储(链栈)

初始化空栈

top = null;

判栈空

if(top == null) return true;
return false;

入栈

Node.next = top;
top = Node;

出栈

int pop(){  
  if(isEmpty()) return -1;  
  top = top.next;
  }

读栈顶元素

int Top(){
  if(isEmpty()) return -1;  
  return a[top];
}

共享栈:top0 == top1 - 1

4.队列

先进先出,后进后出
队尾插入,队头删除
入队和出队不会遍历元素

4.1队列的顺序存储

int N = 6;
int[] q = null;
int front,rear;//头指针,尾指针
int size;

初始化

int[] q = new int[N];
front = rear = 0;
size = 0;

判空

if(front == rear) return true;
return false;

入队

q[rear] = x;
rear++;//尾指针往后移
size++;

出队

if(isEmpty()) return -1;
front++;//头指针往后移
size--;

读队头元素

if(isEmpty()) return -1;
return q[front];

循环队列
入队和出队不需要移动元素

(rear + 1) % N;
(front + 1) % N;
//判空
size == 0;//空
size == N;//满

4.2队列的链式存储

Node head = null,tail = null;//头结点,尾结点

初始化

head = tail = new Node();
head.next = null;

判空

if(head == tail)

入队

node.next = tail.next;
tail.next = node;
tail = node;

出队

if(head == tail) return -1;
if(head.next = tail){
  //队列只有一个元素
  Node s = head.next;
  head.next = s.next;
  tail = head;//把队尾指针修改为队头指针
  return s;
}
Node s = head.next;
head.next = s.next;//头插法
return s;

获取对头元素

if(head == tail) return -1;
return head.next;

5.串

由字符构成的有限序列,是一种特殊的线性表

空串:长度为0

空格串:由一个或多个空格构成

子串:由串中任意长度的连续字符构成

主串:含有子串的串

空串是任意串的子串

5.1串的匹配模式

1.朴数

int Index(String n, String m) {
    int k = 1, i = 1, j = 1;    
    while (i <= n.length && j <= m.length) {      
    char a = n.chatAt(i - 1);      
    char b = m.chatAt(j - 1);      
    if (a == b) {        
      i++;        
      j++;      
    } else {        
      k++;        
      i = k;        
      j = 1;      
    }    
  }    
  if (j > m.length) {      
    return k;    
  } else {      
    return -1;    
  }  
}
时间复杂度时间次数
最好O(m)m次
最坏O(n*m)(n-m+1)*m
平均O(n+m)(n+m)/2

2.KMP算法

串的前缀:包含第一个字符且不包含最后一个字符的子串
串的后缀:包含最后一个字符且不包含第一个字符的子串

第i个字符的next值:1~i-1串中,最长相等前后缀长度+1

next[1] = 0
next[2] = 1

时间复杂度:O(n+m)

6.数组

1.一维数组
在这里插入图片描述
Loc:首地址
L:元素大小

2.二维数组
a(N,M)

行优先
在这里插入图片描述
列优先
在这里插入图片描述

7.矩阵

7.1对称矩阵 (aij = aji)

按行优先:存储下三角区+主对角线
在这里插入图片描述

7.2三对角矩阵

在这里插入图片描述
在这里插入图片描述

7.3稀疏矩阵

稀疏矩阵的顺序存储结构称为三元组顺序表,常见的是十字链表

8.树(非线性)

度:子树的个数为该结点的度

分支结点:度不为0的结点

树的高度(深度):一棵树的最大层树

8.1性质

1.树的性质

1.树中的结点总数=树中所有结点的度数之和+1

2.度为m的数中第i层上至多有mⁱ⁻¹个结点

3.高度为h度为m的数至多有(mʰ-1)/(m-1)

4.具有n个结点,度为m的数的最小高度是
在这里插入图片描述
2.二叉树的性质

二叉树中的结点区分左右子树且最大度为2

1.二叉树第i层上至多有2ⁱ⁻¹个结点

2.高度为h的二叉树至多有2ʰ-1个结点

3.度为0的结点=度为2的结点数+1

4.具有n个结点的完全二叉树的高度为
[log₂n]向下取整+1或[log₂(n+1)]向上取整

8.2满二叉树与完全二叉树

满二叉树:高度为k的二叉树有2ᵏ-1个结点

完全二叉树:除了最后一层外,其余层是满的,在最后一层上,结点必须从左往右,不可以留空
在这里插入图片描述
具有n个结点的二叉树有几种
在这里插入图片描述

8.3二叉树的存储

1.顺序存储

总结点为n,编号为i的结点
i = 1,为根结点,i > 1,该结点的双亲结点为
i / 2向下取整
2i < n,2i为该结点的左孩子,反之没有左孩子
2i + 1 < n,2i+1为该结点的右孩子,反之没有

最坏情况:一个深度为k且只有k个结点的二叉树,需要2ᴷ-1个存储单元

2.链式存储
在这里插入图片描述
二叉链表:n个结点有2n个指针域,n-1个有效指针域,n+1个空指针域

三叉链表:n个结点有n+2个空指针域

完全二叉树采用顺序存储
一般二叉树采用链式存储

8.4二叉树的遍历

先序遍历:根左右
中序遍历:左根右
后序遍历:左右根
层次遍历:从上到下,从左到右

8.5平衡二叉树

二叉树中任意一个结点的左右子树高度之差的绝对值不超过1

叶子结点满足平衡二叉树的要求

完全二叉树一定是平衡二叉树
平衡二叉树不一定是完全二叉树

8.6二叉排序树(二叉查找树)

关键字:根结点
>左子树的所有结点
<右子树的所有结点
左右子树也是二叉排序树

中序遍历的遍历得到的序列是有序序列

8.7最优二叉树(哈夫曼树)

是带权路径长度最短的树

WPL=叶子结点的带权路径长度之和

只有度为0和度为2的结点,没有度为1的结点

总结点:2n-1个

构造哈夫曼树:
从前往后找两个权值最小的
左小右大
加入末尾
权值相同,从前往后

哈夫曼编码:左0右1

等长编码:2x次>=表格中的位数,每个频率*x

压缩比:(等长编码-哈夫曼编码)/等长编码

8.8线索二叉树

有孩子的赋为1,没有孩子赋为0

9.图(线性结构)

任意两个结点之间都可能有直接关系
前驱和后继的数目没有限制

G(V,E)
G是图
V是图中顶点的非空有限集合
E是图中边的有限集合

1.有向图:图中的每条边都是有方向的<>

2.无向图:图中的每条边都是无方向的()

3.完全图:每个顶点与其它n-1个顶点之间有边
无向完全图:[n(n-1)]/2条边
有向完全图:n(n-1)条边

度(D):与该顶点相关的边的数目
总度数=2e//e指边的个数

连通图(无向):任意两个顶点都是连通的
最少:n-1条边,最多:[n(n-1)]/2条边
强连通图(有向)
最少:n条边,最多:n(n-1)条边

9.1图的存储结构

1.邻接矩阵表示法
图(0,1表示),网(♾️,权值表示)
在这里插入图片描述
无向图的邻接矩阵是对称的,有向图不一定
无向图:顶点的度是第i行中不为0的元素个数
有向图:i行是出度,j列是入度

非0元素个数:有向图e个,无向图2e个

2.邻接链表表示法
在这里插入图片描述
稠密图用邻接矩阵,稀疏图用邻接链表

9.2图的遍历

1.深度优先(DFS):访问邻接点(一个),回溯

2.广度优先(BFS):访问邻接点(全部),回溯

时间复杂度:
邻接矩阵:O(n²)
邻接链表:O(n+e)

9.3拓扑排序

1.AVO网不可以构成环路

2.排序:
①选择一个入度为0的结点,输出
②从网中删除该顶点及与该顶点有关的所有弧
③重复上述2步,不存在入度为0的结点
在这里插入图片描述
614325
在有向无环图的拓扑排序中,顶点i在j之前
可能存在弧<i,j>,一定不存在弧<j,i>
可能存在i到j的路径,一定不存在j到i的路径

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值