常见的时间复杂度
O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)
线性表
- 线性存储结构
- 存储位置计算。假设一个数据元素占用的存储空间为c,则LOC(i)=LOC(a1)+(i-1)*c
- 插入:
- 先从最后一个元素开始向前遍历到第i个位置,分别将它们向后移动一个位置
- 将数据插入i处
- 表长+1
- 备注:这样才不会出现覆盖的情况
- 删除:
- 先取出删除元素;
- 从删除元素开始遍历到最后一个位置,分别将它们前移一位
- 表长-1
链式存储结构
- 单链表:[头结点]{(数据域:null/附加信息)(头指针:指向第一个节点的地址)}—>[第一个节点]{(数据域)(指针:指向下一个节点的地址)}
- 获得链表第i个数据:
- 声明一个指针p指向第一个结点
- 遍历,知道p为空。
- 查找成功或者null
- 插入:
- 标准插入语句:
p为前,p->next为后,在中间插入s,则s->next=p->next;p->next=s;
- 标准插入语句:
- 删除:
- 标准删除语句:
p为前,p->next为中,p->next-next为后,则p->next = p->next-next;
- 标准删除语句:
- 创建单链表
- 头插法:
head->ai->ai-1->....->a1
- 尾插法:
head->a1->a2->....ai
- 头插法:
- 整表删除
- 获得链表第i个数据:
- 时间性能
- 查找
- 顺序存储结构:O(1)
- 单链表O(n)
- 插入和删除
- 顺序存储结构:O(n) 平均移动表长一半的元素
- 单链表O(1)
- 查找
- 静态链表
- 用数组描述的链表:结构为{[数据],[下一个数据的数组下表]}
- 第一个和最后一个元素数据域不存数组,且,最后一个元素的数组下标存放第一个数组的下标
- 插入:
- arr[0][addr] = 第一个空闲的下标(通过遍历,把后一个元素的下标不断付给arr[0][addr],直到arr[0][addr]为空,此时返回i)
- 此时知道i为空闲的下标。执行三个操作
- 把插入的值赋给第一个空闲的数组,即arr[i][data] = data
- 把插入位置的前一个元素的下标改成i,arr[i][addr] = 插入位置的后一个元素的下标
- 删除:
- 把最后一个元素的地址指向删除位的下一位
- 把数组第一个元素的地址指向删除位,删除位指向下一个空闲位
- 循环链表
- 跟单链表的差异在于,把单链表的尾指针指向头结点的地址,而不是null
- 判断是否循环完的条件为:
p->next=head.原来的则是:p->next=null
双向链表
- 一个节点同时存储前驱和后继;
插入:
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;
删除:
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);
- 单链表:[头结点]{(数据域:null/附加信息)(头指针:指向第一个节点的地址)}—>[第一个节点]{(数据域)(指针:指向下一个节点的地址)}
- 线性存储结构
栈与队列
- 仅在表尾进行插入和删除操作的线性表:栈顶允许插入和删除,另一端为栈底
- 使用下标为0的一端作为栈底最好,因为变化最小
- 当空栈时,top=-1;有一个元素时候,top=0;栈满时,top=len-1;
- 两栈共享空间
- 一个栈的栈底为数组始端,即下标为0处,另外一个栈底为末端,即数组长度n-1处。如果两栈增加元素,就是两个端点向中间延伸;
- 栈满时:top1+1=top2
- 图示:假设数组有10个空间,则
arr[0] arr[1] arrp[2] arr[3] arr[4][top1] arr[5][top2] arr[6] arr[7] arr[8] arr[9]
栈的链式存储结构
- 头指针指向top;空栈时top=null
入栈
1. s = malloc() e为新增数据 栈为Z 2. s->data = e 3. s->next = Z->top; 4. Z->top = s; 5. Z->count++;
出栈
1. e = Z->top-data 2. p = Z-top 3. Z->top = Z->top->next; 4. free(p); 5. Z->count--; ps:出栈入栈时间复杂度都为O(1)
- 栈的应用->递归/四则运算表达式求值(后缀(逆波兰))
- 逆波兰(所有的符号都要在运算数字后面出现)
- 中缀表达式(正常表达形式)转后缀表达式
- 规则:从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,成为后缀表达式的一部分;要是符号,则判断其与栈顶符号的优先级,是右括号或者优先级低于栈顶符号(乘除优先于加减)则栈顶元素一次出栈并且输出,并将当前符号进栈,一直到最后输出后缀表达式为止;
- 队列:只允许在一端进行插入操作,另一端进行删除操作的线性表:先进先出
- 循环队列
- front指向首个元素,rear指向末尾下一位空元素 flao=0/判断是空队列还是满队列
- 图示:a0(front) a1 a2 a3 a4 null(rear)
- 循环队列主要解决假溢出的问题,后面满了,就再从头开始
- 队列的链式存储
- 循环队列
查找
顺序查找
for(i = 1;i<=n;i++) { if(a[i] == key) return i; }
顺序查找优化
a[0] = key;i=n; while(a[i]!=key) { i--;//ps:通过设置哨兵来减少判断越界这一操作 }
有序表查找
折半查找
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; }
- 插值查找(折半查找进行优化) 把mid =low+(high-low)(key-a[low])/a(a[high]-a[low]);*
- 斐波那契查找
- 关键在于定义个斐波那契数组,根据该数组进行一些列判断。
- 平均性能优于折半查找
- 线性索引查找
- 稠密索引 无序记录(eg:本子记录物品存放位置)
- 分块索引 块内无序 快间有序(eg:图书馆藏书) 用于数据库查找
- 倒排索引 次关键码、记录号表:利用次关键码来定位,找到记录号表中的记录号,取出记录
二叉排序树
- 性质:
- 若它的左子树不空,则左子树上的所有节点的值均小于它的根节点的值
- 若它的右子树不空,则右子树上所有的节点的值均大于它的根节点的值
查找
//二叉树的二叉链表节点结构定义 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次。即根节点就是查找的。
- 查找效率取决于树的形状。
- 性质:
- 平衡二叉树
- 定义:是一种二叉排序树,其中每一个节点的左子树和右子树的高度差至多等于1
排序
- 冒泡排序 复杂度O(n2)
- 简单选择排序o(n2) 先找到最小的,比较i和min,i!=min则交换
- 直接插入排序O(n2)
- 希尔排序O(log2/3n)
- 堆排序O(nlogn)
- 归并排序O(n+logn)
快速排序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; }
数据结构基础知识
最新推荐文章于 2019-01-25 00:23:24 发布