数据结构基础知识

  1. 常见的时间复杂度

      O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)
    
  2. 线性表

    1. 线性存储结构
      1. 存储位置计算。假设一个数据元素占用的存储空间为c,则LOC(i)=LOC(a1)+(i-1)*c
      2. 插入:
        1. 先从最后一个元素开始向前遍历到第i个位置,分别将它们向后移动一个位置
        2. 将数据插入i处
        3. 表长+1
        4. 备注:这样才不会出现覆盖的情况
      3. 删除:
        1. 先取出删除元素;
        2. 从删除元素开始遍历到最后一个位置,分别将它们前移一位
        3. 表长-1
    2. 链式存储结构

      1. 单链表:[头结点]{(数据域:null/附加信息)(头指针:指向第一个节点的地址)}—>[第一个节点]{(数据域)(指针:指向下一个节点的地址)}
        1. 获得链表第i个数据:
          1. 声明一个指针p指向第一个结点
          2. 遍历,知道p为空。
          3. 查找成功或者null
        2. 插入:
          1. 标准插入语句:p为前,p->next为后,在中间插入s,则s->next=p->next;p->next=s;
        3. 删除:
          1. 标准删除语句: p为前,p->next为中,p->next-next为后,则p->next = p->next-next;
        4. 创建单链表
          1. 头插法:head->ai->ai-1->....->a1
          2. 尾插法:head->a1->a2->....ai
        5. 整表删除
      2. 时间性能
        1. 查找
          1. 顺序存储结构:O(1)
          2. 单链表O(n)
        2. 插入和删除
          1. 顺序存储结构:O(n) 平均移动表长一半的元素
          2. 单链表O(1)
      3. 静态链表
        1. 用数组描述的链表:结构为{[数据],[下一个数据的数组下表]}
        2. 第一个和最后一个元素数据域不存数组,且,最后一个元素的数组下标存放第一个数组的下标
        3. 插入:
          1. arr[0][addr] = 第一个空闲的下标(通过遍历,把后一个元素的下标不断付给arr[0][addr],直到arr[0][addr]为空,此时返回i)
          2. 此时知道i为空闲的下标。执行三个操作
            1. 把插入的值赋给第一个空闲的数组,即arr[i][data] = data
            2. 把插入位置的前一个元素的下标改成i,arr[i][addr] = 插入位置的后一个元素的下标
        4. 删除:
          1. 把最后一个元素的地址指向删除位的下一位
          2. 把数组第一个元素的地址指向删除位,删除位指向下一个空闲位
      4. 循环链表
        1. 跟单链表的差异在于,把单链表的尾指针指向头结点的地址,而不是null
        2. 判断是否循环完的条件为:p->next=head.原来的则是:p->next=null
      5. 双向链表

        1. 一个节点同时存储前驱和后继;
        2. 插入:

          1. 假设把s插入ai和ai+1中 位置关系为:[p] [s] [p->next]
          2. s->prior = p;
          3. s->next = p->next;
          4. p->next->prior = s;
          5. p->next = s;
          
        3. 删除:

          1. 假设要把ai从ai-1和ai+1中删除,位置关系为:[p->prior] [p] [p->next]
          2. p->prior->next = p->next;
          3. p->next->prior = p-prior;
          4. free(p);  
          
  3. 栈与队列

    1. 仅在表尾进行插入和删除操作的线性表:栈顶允许插入和删除,另一端为栈底
    2. 使用下标为0的一端作为栈底最好,因为变化最小
    3. 当空栈时,top=-1;有一个元素时候,top=0;栈满时,top=len-1;
    4. 两栈共享空间
      1. 一个栈的栈底为数组始端,即下标为0处,另外一个栈底为末端,即数组长度n-1处。如果两栈增加元素,就是两个端点向中间延伸;
      2. 栈满时:top1+1=top2
      3. 图示:假设数组有10个空间,则
        1. arr[0] arr[1] arrp[2] arr[3] arr[4][top1] arr[5][top2] arr[6] arr[7] arr[8] arr[9]
    5. 栈的链式存储结构

      1. 头指针指向top;空栈时top=null
      2. 入栈

        1. s = malloc() e为新增数据 栈为Z
        2. s->data = e
        3. s->next = Z->top;
        4. Z->top = s;
        5. Z->count++;
        
      3. 出栈

        1. e = Z->top-data
        2. p = Z-top
        3. Z->top = Z->top->next;
        4. free(p);
        5. Z->count--;
        ps:出栈入栈时间复杂度都为O(1)
        
      4. 栈的应用->递归/四则运算表达式求值(后缀(逆波兰))
        1. 逆波兰(所有的符号都要在运算数字后面出现)
        2. 中缀表达式(正常表达形式)转后缀表达式
          1. 规则:从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,成为后缀表达式的一部分;要是符号,则判断其与栈顶符号的优先级,是右括号或者优先级低于栈顶符号(乘除优先于加减)则栈顶元素一次出栈并且输出,并将当前符号进栈,一直到最后输出后缀表达式为止;
      5. 队列:只允许在一端进行插入操作,另一端进行删除操作的线性表:先进先出
        1. 循环队列
          1. front指向首个元素,rear指向末尾下一位空元素 flao=0/判断是空队列还是满队列
          2. 图示:a0(front) a1 a2 a3 a4 null(rear)
          3. 循环队列主要解决假溢出的问题,后面满了,就再从头开始
        2. 队列的链式存储
  4. 查找

    1. 顺序查找

      for(i = 1;i<=n;i++)
      {
          if(a[i] == key) return i;
      }
      
    2. 顺序查找优化

      a[0] = key;i=n;
      while(a[i]!=key)
      {
          i--;//ps:通过设置哨兵来减少判断越界这一操作
      }
      
    3. 有序表查找

      1. 折半查找

        int Binary_search(int *a,int n,int key)
        {
            int low,high,mid;
            low=1;
            high=n;
            while(low<=high)
            {
                mid = (low+high)/2;
                if(key<a[mid])
                {
                    high=mid-1;
                }
                else if(key>a[mid])
                {
                    low=mid+1;
                }
                else
                    return mid;
            }
            return 0;
        }
        
    4. 插值查找(折半查找进行优化) 把mid =low+(high-low)(key-a[low])/a(a[high]-a[low]);*
    5. 斐波那契查找
      1. 关键在于定义个斐波那契数组,根据该数组进行一些列判断。
      2. 平均性能优于折半查找
    6. 线性索引查找
      1. 稠密索引 无序记录(eg:本子记录物品存放位置)
      2. 分块索引 块内无序 快间有序(eg:图书馆藏书) 用于数据库查找
      3. 倒排索引 次关键码、记录号表:利用次关键码来定位,找到记录号表中的记录号,取出记录
    7. 二叉排序树

      1. 性质:
        1. 若它的左子树不空,则左子树上的所有节点的值均小于它的根节点的值
        2. 若它的右子树不空,则右子树上所有的节点的值均大于它的根节点的值
      2. 查找

        //二叉树的二叉链表节点结构定义
        typedef struct BITNode
        {
            int data;//节点数据
            struct BITNode *lchild,*rchild;//左右孩子指针
        }BITNode,*BITree;
        /*递归查找二叉排序树T中是否存在key
        指针f指向T的双亲,初始值为null
        若查找成功,则指针指向改数据元素节点,并返回TRUE;
        否则指针P指向查找路径上访问的最后一个节点并返回false;*/
        Status SearchBST(BITree T,int key,BITree f,BITree *p)
        {
            if(!T)
            {
                *p = f;
                return false;
            }
            else if(key == T->data)
            {
                *p = T;
                return true;
            }
            else if(key<T->data)
                return SearchBST(T->lchild,key,T,p);
            else
                return SearchBST(T->rchild,key,T,p);
        }
        

      3.插入

      Status InsertBST(BiTree *T,int key)
      {
          BiTree p,s;
          if(!SearchBST(*T,key,null,&p))//查找不成功
          {
              s = (BiTree)malloc(sizeof(BITNode));
              s->data = key;
              s->lchild = s->rchild = null;
              if(!p)
                  *T=s;
              else if(key<p->data)
              {
                  p->lchild = s;
              }
              else
              {
                  p->rchild = s;
              }
              return true;
          }
          else
              return false;
      }
      所以构建成为一个二叉排序树就很简单了
      int i;
      int a[10] = {62,88,58,47,35,73,51,99,37,93};
      BiTree T = null;
      for(i=0;i<10;i++)
      {
          InsertBST(&T,a[i]);
      }
      
      1. 删除
      2. 总结:
        1. 查找走的是根节点到要查找的节点的路径,其比较次数等于给定值的节点在二叉排序树的层数。
        2. 极端情况为1次。即根节点就是查找的。
        3. 查找效率取决于树的形状。
    8. 平衡二叉树
      1. 定义:是一种二叉排序树,其中每一个节点的左子树和右子树的高度差至多等于1
  5. 排序

    1. 冒泡排序 复杂度O(n2)
    2. 简单选择排序o(n2) 先找到最小的,比较i和min,i!=min则交换
    3. 直接插入排序O(n2)
    4. 希尔排序O(log2/3n)
    5. 堆排序O(nlogn)
    6. 归并排序O(n+logn)
    7. 快速排序O(nlogn)

      void QuickSort(SqList *L)
      {
          QSort(L,1,L->length);//1和length代表当前待排序的序列的最小下标值low和最大下标值high
      }
      vodi QSort(SqList *L,int low,int high)
      {
          int pivot;
          if(low<high)
          {
              pivot = Partition(L,low,high);//江L->r[low...high]一分为二,算出枢轴值pivot
              QSort(L,low,pivot-1);对低子表递归排序
              QSort(L,pivot+1,high);对高字表递归排序
          }
      }
      int Partition(SqList *L,int low,int high)
      {
          int pivotkey;
          pivotkey = L->r[low];
          while(low<high)
          {
              while( low<high && L->r[high]>=pivotkey)
                  high--;
              swap(L,low,high);
              while(low<high && L->r[low]<=pivotkey)
                  low++;
              swap(L,low,high);
          }
          return low;
      }
      
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值