数据结构概述

数据结构

程序性能

时间空间复杂性分析方法

  1. 时间复杂性
    S(P)= C + Sp
    C表示一个常亮,表示固定需要的时间,Sp表示可变部分所需的空间
  2. 时间复杂性
    T(P) = 编译时间 + 运行时间(tp)
  3. 大写O符号:给出函数f的一个上限
    Ω符号:与大O符号类似,它用来估算f的下限值

2种搜索方法

  • 顺序搜索:无序数组搜索
    template<class T>
    int SequentialSearch(T a[], const T& x, int n)
    {
        int i;
        for(int i=0; i<n; i++)
            if(i == n) return -1;
            return i;
    }
  • 折半搜索:有序数组查找元素
template<class T>
int BinarySearch(T a[], const T& x, int n)
{
    int left=0, right=n-1;
    while(left <=right)
    {
        int middle = (left+right)/2;
        if(a[middle] == x) return middle;
        if(x>a[middle]) left = middle+1;
        else right = middle-1;
    }
    return -1;
}

4种排序方法

排序方法 计数排序 选择排序 冒泡排序 插入排序
元素交换次数 2n 3n-1 0~n-1 0~n-1
元素比较次数 (n-1)n/2 (n-1)n/2 (n-1)n/2 (n-1)n/2

- 计数排序

template<class T>
void Rearrenge(T a[], int n, int r[]) //r[]为排名
{
    T *u = new T[n+1];
    for(int i=1; i<n; i++)
        u[r[i]] = a[i];
    for(int i=1; i<n; i++)
        a[i] = u[i];
    delete [] u;
}
  • 选择排序
template<class T>
void SelectSort(T a[], int n)
{
    for(int size=n; size>1; size--)
    {
        int j = Max(a,size);
        Swap(a[j], a[size-1]);
    }
}
  • 冒泡排序
template<class T>
void Bubble(T a[], int n)
{
    for(int i=0; i<n; i++)
        if(a[i]>a[j+1]) Swap(a[i],a[i+1]);
}

void BubbleSort(T a[],int n)
{
    for(int i=n; i>1; i--)
        Bubble(a,i);
}
  • 插入排序
template<class T>
void Insert(T a[], int n, const T&x)
{
    int i;
    for(i=n-1; i>1&&x<a[i]; i--)
        a[i+1] = a[i];
    a[i+1] = x;
}

void InsertSort(T a[], int n)
{
    for(int i=1; i<n; i++)
    {
        T t = a[i];
        Insert(a,i,t);
    }
}

程序性能

描述方法比较

描述方法 公式化描述 链表描述 间接寻址
查找第k个元素 ⊖(1) O(k) ⊖(1)
删除第k个元素 O((n-k)s) O(k) O(n-k)
在第k个元素后插入 O((n-k)s) O(k+s) O(n-k)

s和n分别表示sizeOf(T)和链表长度

应用

  • 箱子排序
    在箱子排序过程中,节点首先被放入箱子之中,具有相同分数的节点都放在同一个箱子中,然后通过把箱子链接起来就可以创建一个有序的链表

数组和矩阵

几种特殊矩阵的存储

  • 对角矩阵
    4 0 0 0 0
    0 2 0 0 0
    0 0 7 0 0
    0 0 0 3 0
    0 0 0 0 2

    由于一个对角矩阵最多包含n个非0元素,可以采用一维数组来描述:

    T d[n]
  • 三对角矩阵
    2 1 0 0
    3 1 3 0
    0 5 2 7
    0 0 9 0

    在一个n*n三对角矩阵T中,非0元素排列在三条对角线上:
     1. 主对角线 —— i=j
     2. 低对角线 —— i-j=1
     3. 高对角线 —— i-j=-1
    元素总数:3n-2
  • 三角矩阵
    2 1 3 0     2 0 0 0
    0 1 3 8     4 1 0 0
    0 0 1 6     6 9 4 0
    0 0 0 0     0 5 7 0
   上三角矩阵    下三角矩阵

   元素个数:n(n+1)/2
   只存储上三角或下三角即可

堆栈

基本概念

堆栈是一个线性表,是一个后进先出的数据结构(LIFO)

插入和删除操作都在表的同一端进行,一端称为栈顶top,另一端称为栈底bottom

应用

  • 括号匹配
    在这个问题中将要一个字符串的左、右括号:
    思路:
    在从左到右扫描字符串的过程中,每当遇到左括号就将其放于堆栈中,每当遇到右括号时就从栈顶删除一个左括号

队列

基本概念

**队列是一个线性表,是一个先进先出(FIFO)的数据结构** **其插入和删除操作分别在不同端进行,添加新元素的一端称为队尾rear,删除元素的一端称为队首front**

如何判断队列是否已满

  • 公式化描述
    1.location(i)=location(1)+i-1描述:若rear=Maxsize-1且rear>0时,则队列未满
    2.location(i)=(location(1)+i-1)/Maxsize描述:rear=front(队列满或队列空,若满会报错)
bool IsFull() const
{
    return (((rear+1)%MaxSize==front)?1:0);
}
  • 链表描述
//如果队列满会报错 
template<class T>
bool LinkedQueue<T>::IsFull() const
{
    Node<T> *p;
    try
    {
        p = new Node<T>;
        delete p;
        return false;
    }catch(NoMem)
    {
        return true;
    }
}

跳表和散列

字典概念

字典是一些元素的集合,每个元素有一个称作key的域,不同元素的key各不相同

字典的线性表描述

  • 查找
  • 插入
  • 删除

字典的跳表描述

  • 级的分配
    1.假设一随机数产生器产生随机数0~RAND_MAX,则下次产生的随机数小于等于CutOff=p*RAND_MAX的概率为p
    2.级MaxLevel的最大值为⌈log(1/p) N⌉-1

  • 在跳表中搜索

template<class E, class K>
bool SkipList<E,k>::Search(const K& k, E& e)
{
    if(k>=TailKey) return false;
    SkipNode<E,K> *p = head;

    for(int i=Levels; i>=0; i--)
        while(p->link[i]->data<k)
            p = p->link[i];

    e = e->link[0]->data;
    return (e==k);
}
  • 复杂性
    当元素为n时:
    搜索、插入、删除操作的复杂性均为O(n+MaxLevel)
    平均复杂性均为O(logn)

散列表描述

理想散列

是用一个散列函数把关键字映射到散列表中的特定的位置,如果元素e的关键字为k,散列函数为f,那么e在散列表中的位置为f(k)

线性开型寻址散列

  • 方法
    除法散列函数:f(k) = k % D

  • 插入
    如果发生了碰撞(溢出),将插入到起始桶后面的空桶中;
    在寻找下一个可用桶的过程中,表被视为环形的

  • 搜索
    首先搜索起始桶,会发生下面情况:
    1.存有关键词K的桶找到
    2.到达空桶
    3.回到f(k)
    若发生2.3,则说明表中没有关键字为K的元素

  • 删除(会带来多个元素的移动)
    1.一种策略:
    从欲删除的元素开始逐个检查每个桶,以确定要移动的元素
    2.另一种:
    为每个桶增加NeverUsed域

二叉树和其他树

二叉树特性

  • 包含n个元素的二叉树的边数为n-1

  • 若二叉树的高度为h,则元素最少为h,最多为2^h - 1

二叉树公式化描述

当缺少很多元素时,很浪费空间,一个有n个元素的二叉树可能最多需要2^n - 1个空间

二叉树链表描述的操作

  • 前序遍历:先访问根节点,然后访问左子树、右子树

  • 中序遍历:先访问左子树,然后访问根节点、右子树

  • 后序遍历:先访问左子树、右子树,然后访问根节点

优先队列

线性表描述

  • 性能:
    无序线性表
    插入所需时间:⊖(1)
    删除所需时间:⊖(n)
    有序线性表
    插入所需时间:⊖(n)
    删除所需时间:⊖(1)
    链表
    插入所需时间:⊖(1)
    删除所需时间:⊖(n)

最大堆的插入

  • 与堆中插入后最后一个元素的父节点比较,如果比其小,则可做其孩子,如果比其大,则移向父节点
  • 时间复杂性为O(logn)
template<class T>
MaxHeap<T>& MaxHeap<T>::Insert(const T& x)
{
    if(CurrentSize == MaxSize)
        throw NoMem();

    int i = ++CurrentSize;
    while(i!=1 && x>heap[i/2])
    {
        heap[i] = heap[i/2];
        i /= 2;
    }
    heap[i] = x;
    return *this;
}

最大堆的删除

  • 从最大堆中删除一个元素时,删除根部元素

  • 时间复杂性为O(height) = O(log2 n)

template<class T>
MaxHeap<T>& MaxHeap<T>::Delete(T &x)
{
    if(Current == 0)
        throw OutOfBounds();
    x = heap[1];
    T y = heap[CurrentSize-];
    int i = 1, ci = 2;
    while(ci<=CurrentSize)
    {
        //heap[ci]应该为较大的孩子
        if(ci<Currentsize && heap[ci]<heap[ci+1])
            ci++;
        if(y>heap[ci]) break;   //能把y放入heap[i]
        //不能
        heap[i] = heap[ci];
        i = ci;
        ci *= 2;
    }
    heap[i] = y;
    return *this;
}

最大堆的初始化

  • 为了将完全二叉树转化为最大堆,从第一个具有孩子的节点开始,位置为i=[n/2],如果以这个元素为根的子树已是最大堆,此时不需调整,否则需要调整使之称为堆,随后继续检查i-1,i-2等节点,直至检查至根节点

  • 时间复杂性O(nlongn)

template<class T>
void MaxHeap<T>::Initialize(T a[], int size, int ArraySize)
{
    delete heap;
    heap = a;
    CurrentSize = size;
    MaxSize = ArraySize;

    for(int i=CurrentSize/2; i>=1; i--)
    {
        T y = heap[i];

        int c = 2*i;
        while(c<=CurrentSize)
        {
            if(c<CurrentSize && heap[c]<heap[c+1])
                c++;
            if(y>=heap[c]) break;
            heap[c/2] = heap[c];
            c *= 2;
        }
        heap[c/2] = y;      
    }
}

应用

  • 堆排序

首先将一个数组初始化为一个最大堆,然后从堆中依次删除元素,然后各元素就以递减的顺序排好序了,时间复杂度为O(nlogn)

  • 霍夫曼树

为了构造霍夫曼树,首先从仅含一个外部节点的二叉树集合开始,每个外部节点代表字符串中一个不同的字符,其权重等于盖子负的频率,此后不断地从集合中选择两棵具有最小权重的二叉树,并合并为一棵二叉树,合并方法是把这两棵二叉树分别作为左右子树然后增加一个新的根节点,新二叉树权重为两子树之和,该过程持续至只剩一棵树

搜索树

二叉搜索树

  • 搜索
    假设需要寻找关键值为K的元素,如果根为空,失败;如果K小于根节点关键值,那么在左子树中搜索;反之,在右子树中搜索
    时间复杂性:O(nlogn)

  • 插入
    若插入元素e,先要验证e的关键值与树中已有关键字是否重合

  • 删除
    情况1:
    p是树叶,直接丢弃树叶节点
    情况2:
    p只有一个子树,若p是根节点,则根节点直接指向p的孩子,然后删除p;若p有父节点,则将p父节点指向p的孩子,然后删除p
    情况三:
    p有左右子树,需要将该元素替换为左子树中最大元素或者右子树的最小元素

AVL树

  • 搜索(同二叉树)
    时间复杂性O(nlogn)

  • 插入
    LL、LR、RR、RL型不平衡

  • 删除
    R0、R1、R-1;L0、L1、L-1

By Yuanhang, Luo

[email protected]

你可能感兴趣的:(数据结构,c)