数据结构(严蔚敏版)学习要点总结(一)

    xiaoxiao2025-06-02  41

    一、数据结构基本概念 1.数据:对客观事物的符号表示  数据元素:数据的基本单位  数据对象:性质相同的 数据元素 的集合  数据结构:相互之间存在一种或多种特定关系的数据元素的集合 2.数据元素之间的关系由两种表示:顺序映像和非顺序映像->顺序存储和链式存储 3.数据类型:是一个 值的集合 和定义在这个值集上的 一组操作 的总称 4.抽象数据类型:指一个数学模型以及定义在该模型上的一组操作    包括:定义,表现和实现三部分。(D,S,P)分别表示数据对象,关系集合,操作集合    分为:原子类型、固定聚合类型、可变聚合类型(长度可变)、多形数据类型 5.算法:指令的有限序列,包括:有序性、确定性、可行性、输入、输出 6.时间复杂度:一般指最坏情况下的时间复杂度 二、.线性表    ①两个线性表LA,LB合并算法(伪代码) ——ExistElem()执行时间为O(LB),所以总该为O(LA*LB)    void union(List &La,List Lb){        La_len = La.length();        Lb_len = Lb.length();        for(i = 1;i<=Lb.length();i++){        Elem e  = GetElem(Lb,i); if(!ExistElem(La,e))   ListInsert(La,++La_len,e);        }       }    ②两个递增有序排列,将LA与LB归并成一个LC,使得LC仍然递增——执行时间为O(LA+LB)    // 递增排序 private static ArrayList<Integer> unionSort(ArrayList<Integer> la, ArrayList<Integer> lb) { ArrayList<Integer> lc = new ArrayList<>(); int i = 0, j = 0; while (i < la.size() && j < lb.size()) { if (la.get(i) <= lb.get(j)) { lc.add(la.get(i)); i++; } else { lc.add(lb.get(j)); j++; } } //如果la或lb还有剩余未比较的,全部加入即可 while(i<la.size()){ lc.add(la.get(i)); i++; } while(j<lb.size()){ lc.add(lb.get(j)); j++; } return lc; }   ③ 线性表的顺序实现:    typedef struct{   ElemType *elem;        int length;   //表长        int listSize;  //元素个数  } SqList;     注意:  顺序线性表中插入新的元素在指定位置时,需要将其后的位置统一后移并增长表长;     删除则需要把第i个元素统一向前移动;时间复杂度均为O(n)   ④线性表的单链式实现:    typedef struct LNode{      ElemType data;      struct LNode *next;    }LNode , *LinkList;     *  p = p->next; 用此方法可以找到第i个元素  *  实现插入:1. s->next = p->next; 2.p->next = s; (s为新结点,p为插入位置)  *  删除b元素只需 b的前一个结点p 使得  p->next = p->next->next;  *  带表头结点的单链表创建(L-游标),使得 p(新结点)->next = L->next ; //插入 L->next=p; //游标移动  ⑤静态链表的结构: #define MAX_SIZE = 1000 ; typedef struct {      ElemType data ;      int cur ; }Component , StaticLinkList[MAX_SIZE]; 首先是下标为0的结点中不包含有意义的数据,它的cur存储的是备用链表(即没有存储的数据的那些结点)第一个结点的下标。其次是数组最后一个元素,数组最后一个元素的cur存放的是静态链表的第一个结点的下标。最后就是链表的最后一个元素(并不一定是数组的最后一个元素),链表最后一个元素的cur一般存放0,表示它后面的结点为空了。  ⑥循环链表 表中最后一个结点的指针域指向头结点,整个链表形成一个环。与线性链表基本一致,差别在于算法中的循环条件不再是p或p->next是否为空,而是他们是否等于头指针。  ⑦双向链表  typedef struct DuLNode{           ElemType data;           struct DuLNode *prior;           struct DuLNode *next;  }DuLNode,*DuLinkList; 注意:插入、删除时需要修改两个方向上的指针,复杂度为O(n) a,b,c删除时需要把 b.prior.next = b.next;   b.next.prior = b.prior; a,c插入x时需要把 s.data = e; s.prior = p.prior; p.prior.next = s ; s.next = p; p.prior = s; 具体画图即明白。  ⑧线性链表 typedef struct LNode{    //结点类型       ElemType data;       struct LNode *next; } *Link,*Position; typedef struct {     //链表类型        Link head,tail;     //链表头尾结点        int len;    //线性表中的数据元素个数 }LinkList; 三、栈和队列 1.栈:后进先出(LIFO) 顺序栈: typedef struct {       SElemType *base;    // 栈底       SElemType *top;      // 栈顶       int stackSize;    //当前已分配的存储空间 }SqStack;   获取栈顶: e = *(S.top-1);    插入元素 : *S.top++=e;  弹出元素: e = *--S.top; 2.栈的应用  ①数制转换:   原理:N=(N 整除 d) * d + N (mod) d         //关键代码,将N转换为radix进制            public static void getRadix(int N,int radix){ Stack<Integer> stack = new Stack<>(); while(N!=0){ stack.push(N%radix); N=N/radix; } while(!stack.isEmpty()){ System.out.print(stack.pop()); } }   ②括号匹配检验(没加入一个封闭括号就看之前是否有开放括号,若检查到空都没则失败;有则消解,继续加入)   ③行编译程序     ④迷宫求解(利用栈的可回退):若当前位置通则纳入当前路径,并朝下一位置探索;若当前不通,则退回到前一位置朝其他方向的下一位置探索,若四周均不同则删除该路径。直到找到出口位置。   ⑤表达式求值   ⑥栈与递归的实现      I.菲波那切数列 public static int fibonacci(int n){    if(n==0){ return 0;    }else if(n==1){ return 1;    }else{ return fibonacci(n-1)+fibonacci(n-2);   } }       II.阿克曼函数       public static int ackerman(int n,int m){ if(m==0){ return n+1; }else if(n==0){ return ackerman(m-1, 1); }else{ return ackerman(m-1, ackerman(m, n-1)); }     }     III.汉诺塔函数                //递归调用,永远是先遍历到最深层,再逆序执行下面未执行的操作 public static void hanoi(int n, char a, char b, char c) { if (n == 1) { System.out.println("将"+n+"号盘子从"+a+"移到"+c); } else { hanoi(n - 1, a, c, b); // 将a上编号为1到n-1的盘子移到b System.out.println("将"+n+"号盘子从"+a+"移到"+c); hanoi(n - 1, b, a, c); // 将b上编号为1到n-1的盘子移到c } }   3.队列    先进先出的线性表(FIFO),典型列子就是操作系统的作业排队。    单链队列——队列的链式存储结构     typedef struct QNode{         QElemType data;         struct QNode *next;      }QNode,*QueuePtr;     typedef struct {        QueuePtr front ; //队头指针        QueuePtr rear;    //队尾指针     }LinkQueue;    判断队空:Q.front == Q.rear       循环队列——队列的顺序表示和实现    typedef struct{         QElemType data;          int front;          int rear;    }SqQueue;    注意:判断队满:  (Q.rear + 1)%MAXQSIZE == Q.front    判断队空:Q.front == Q.rear   4.离散事件模拟 四、串  1.串的模式匹配算法    KMP算法是在已知模式串的next函数值的基础上执行的    *next 数组的求解竟然如此简单:就是找最大对称长度的前缀后缀,然后整体右移一位,初值赋为-1   2 KMP的算法流程: 假设现在文本串S匹配到 i 位置,模式串P匹配到 j 位置 如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++,继续匹配下一个字符; 如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]。此举意味着失配时,模式串P相对于文本串S向右移动了j - next [j] 位。     *我们发现如果某个字符匹配成功,模式串首字符的位置保持不动,仅仅是i++、j++;如果匹配失配,i 不变(即 i 不回溯),模式串会跳过匹配过的next [j]个字符。整个算法最坏的情况是,当模式串首字符位于i - j的位置时才匹配成功,算法结束。     所以,如果文本串的长度为n,模式串的长度为m,那么匹配过程的时间复杂度为O(n),算上计算next的O(m)时间,KMP的整体时间复杂度为O(m + n)。  3.应用:文本编辑、建立 词索引表

    转载请注明原文地址: https://ju.6miu.com/read-1299534.html
    最新回复(0)