数据就是能够输入计算机中,并被计算机识别、存储、处理、显示的信息
数据是由数据元素组成
数据元素:是数据的基本单位,数据元素有由若干数据项构成,数据项就是数据元素的最小单位,可以用基本数据类型表示信息的一个属性
1、数据结构的定义数据结构:研究数据与数据之间的逻辑结构,并研究如何在计算机中存储这种有关系和有关系的数据(存储结构),以及对这些有关系的数据进行操作
2、数据之间的关系分类分类一
线性关系 (排队)
层次关系 (上下级)
网状关系(地铁线路)
3、数据结构研究内容研究:逻辑结构、存储结构、数据运算
关系-------逻辑结构逻辑结构是数据之间的抽象关系
四种逻辑结构:集合结构、线性结构、树形结构、图形结构
集合结构所有数据元素之间没有任何关系,只不过这些数据元素有共同的属性、特点以及同属于一个整体
线性结构数据元素之间存在先后顺序。最多1个前驱,一个后继
树形结构数据元素之间是一种层次关系。一个前驱,n个后继
图形结构数据元素之间存在多个关系。n个前驱、n个后继
存储--------存储结构数据逻辑结构在计算机中的映射。就是在计算机中存储数据,并表示出这些数据之间关系
顺序存储---------连续内存空间将数据元素存储在一段连续的空间中,也就是说这些数据在内存中的存储地址是连续的,只需要知道第一个数据元素的地址,就可以知道其他数据元素的地址。例如数组
将数据元素按照逻辑顺序存储在内存中一段连续的空间,利用内存空间的地址来表示先后顺序
链式存储------------数据的存储空间是随机的数据元素在内存中的存储地址是随机的,不一定是连续的,(所以不需要一次性全部申请空间,根据需要来申请空间)需要通过在存储数据元素的同时,将下一个数据元素的地址一并存上。这样任然能够表示数据元素之间的关系。
链式存储 : 存储数据元素 + 关系
索引存储在存储数据元素的同时,还要存储数据的索引在一个索引表中,可以通过索引表得到数据元素之间的关系。例如:电话簿
散列存储通过数元素中的部分关键字,进行某种运算得到该数据元素的地址。
运算--------数据操作增、删、该、查
算法 算法定义算法就是解决某个问题的方法步骤
在计算机中算法是一个有限的语句集合
算法与程序的区别算法就是解决某个问题的方法步骤,程序是在计算机中以计算机语言具体实现
区别:算法不一定要依赖计算机语言,是一种描述方式。程序是必须要依赖计算机语。算法是有限的,程序可以是无穷的。
算法与数据结构算法设计:依赖于数据结构的逻辑结构
算法的实现:依赖于数据结构的存储结构
程序=数据结构+算法
算法的好坏判断- 正确性
- 效率:消耗时间多少、消耗空间多少
- 编程:算法结构好、易于、理解、编写、调试
时间计算:
-
事后统计法
把算法实现,进行运行,得到算法运行消耗的时间。
**缺陷:**依赖于特定的计算机软硬件,进行大量的统计
-
事先估计法
假设所有计算机执行一条指令的时间都是一致的,只需要统计出执行的语句的次数
语句频度:算法中一条语句的重复次数
时间复杂度T(n):所有语句频度之和就是算法的语句执行次数
在循环中一条语句,执行次数不确定,就用问题规模n来表示
通常利用事先估计法来表示算法时间。
-
T(n)表示法:每条语句的语句频度 * 语句条数
-
大O表示法O(n):n代表无穷大,并且取T(n)表达式的最高次幂。如果语句执行次数为常数次,那么O(n)=1;
占用空间的大小
一、线性表 线性表概念多个数据元素的逻辑结构为线性关系,这些数据元素之间存在先后顺序,就叫做线性结构,即是线性表。数组就是一个
线性关系:中有且仅有“第一个数据元素”和一个“最后一个数据元素”,中间的数据元素只有一个前驱和一个后继
在存储线性表时,既需要存储具有线性表的数据元素,也需要存储元素数据之间的关系。
总而言之,线性表就是一种逻辑结构为线性结构关系的逻辑结构
线性表的存储结构线性表的顺序存储:存储数据元素时,把数据元素按照逻辑关系(先后顺序),依次存储在一段连续的内存空间,借助元素在这段连续空间的地址,来表示数据间的先后顺序,这就是顺序结构
顺序表顺序表 = 数据元素逻辑结构为线性表 + 存储结构为顺序存储。
数组就是一个顺序表
顺序表的运算-
添加
-
删除
-
修改
-
查找
-
判断满
-
判断空
-
求表长
-
创建表
-
清空表
- 申请一段连续的内存空间来存储数据,通过地址来表示先后关系
- 记录表中元素的个数
- 要申请一段连续的内存空间,申请的时候大小就已经固定了,空间不能动态的添加、删除,这样比较占用内存资源
- 插入、删除操作比较麻烦,需要移动整片空间
#include链表#include struct array { int num; //struct book Book[50]; //表示为申请了50个连续的空间,每个元素为book int no[20]; //定义一个,int类型的数组来存储每一本书的编号 }; //在堆中动态创建结构体,并返回结构体在堆中的首地址 struct array * creat() { struct array * p=malloc(sizeof(struct array)); if(p==NULL) { printf("内存空间申请失败!n"); return NULL; } p->num=0; return p; } //判断结构体空间是否为空 int empty(struct array *p) { if(p->num == 0) { printf("空间为空n"); return 1; //空间为空返回1 } return 0; } //判断是否空间是否满了 int full(struct array *p) { if(p->num == 20) { printf("空间已满n"); //空间如果满了返回1 return 1; } return 0; //空间如果没满返回0 } //添加新的元素 void add(struct array * p,int pos,int shu) //传入结构体的首地址和需要添加元素的位置、添加的元素的值 { //判断空间是否为满了 if(full(p)) { return ; //如果空间满了,那就自接退出,不操作 } //判断空间是否为空 if(empty(p)) //空间为空,那么就将插入的数据添加到no数组第0个元素位置 { p->no[0]=shu; p->num ++; return ; } //判断插入下标是否越界 if(pos > p->num -1 ) { printf("现在有%d个数据,下标范围是0~%dn",p->num,(p->num) -1); printf("插入的数据下标为%d超出下标范围,将该数据加在最后一个数据元素后面n",pos); putchar('n'); p->no[p->num]=shu; //越界了要将数据添加在最后一个元素后面,而不是覆盖最后一个元素p->no[p->num-1] p->num ++; return ; //插入元素下标越界,结束添加功能 } for(int i = p->num - 1;i >= pos ;i--) //将pos处以及之后的元素后一位,空出来给添加的元素 { p->no[i+1]=p->no[i]; } p->no[pos]=shu; p->num ++; } //删除执行位置的数据元素 void del(struct array *p,int pos,int * k) { //判断是否为空 if(empty(p)) { return ; } //判断下标是否越界 if(pos >= p->num -1) { printf("现在有%d个数据,下标范围是0~%dn",p->num,(p->num) -1); printf("删除的数据下标为%d超出下标范围,将k的值赋值为0n",pos); putchar('n'); *k=0; return ; //插入元素下标越界,结束添加功能 } *k=p->no[pos]; printf("需要删除下标为%d,数据为%dn",pos,*k); for(int i=pos;i< p->num-1;i++) //将pos之后的第一个元素赋值给pos的位置,其他元素一次前移一个位置 { //检验判断的条件是,将条件带入到式子中,最后一次前移是,p->nos[num-2]=p->nos[num-1]; p->no[i]=p->no[i+1]; } printf("删除成功n"); p->num --; } //查找数据元素 void reserch(struct array *p,int pos) { //判断查看的下标是否越界 if(pos > p->num -1) { printf("现在有%d个数据,下标范围是0~%dn",p->num,(p->num) -1); printf("查看的数据下标为%d超出下标范围,自接退出查找n",pos); putchar('n'); return ; //插入元素下标越界,结束添加功能 } printf("查看的数据元素是下标为%d的元素数据%dn",pos,p->no[pos]); } //修改数据元素 void change(struct array *p,int pos,int shu) { //判断查看的下标是否越界 if(pos > p->num -1) { printf("现在有%d个数据,下标范围是0~%dn",p->num,(p->num) -1); printf("查看的数据下标为%d超出下标范围,自接退出修改n",pos); putchar('n'); return ; //插入元素下标越界,结束添加功能 } printf("修改前:%dn",p->no[pos]); p->no[pos]=shu; printf("修改完成n"); printf("修改后:%dn",p->no[pos]); } //数据显示 void show(struct array *p) { for(int i=0;i num;i++) { printf("%d ",p->no[i]); } putchar('n'); } int main() { int k=0; struct array *p=creat(); show(p); add(p,10,1); add(p,10,2); add(p,10,3); add(p,10,4); add(p,10,5); add(p,10,6); show(p); del(p,0,&k); show(p); add(p,4,7); show(p); reserch(p,5); change(p,0,8); show(p); }
链式存储:是一种不需要连续空间存储数据的数据结构,需要动态存储数据内容和数据之间的先后关系。
总而言之:链表就是一种
链表的概念链表是一种数据结构,它的逻辑结构为线性表,存储结构为链式存储。数据元素之间的位置是任意的(需要有这个元素才动态申请存储空间),一个数据元素由数据内容+关系组成。其中关系是通过指针来表示,这个指针存储下一个数据元素的地址。
链表 = 线性表 + 链式存储
链表组成-
头指针:是一个指针,存储链表中第一个元素的地址,方便指名链表的位置,之后可以通过这个指针来访问其他数据元素。
-
首元素节点
真正存储数据的节点
-
头节点
在链表,通常会添加一个不存储数据的节点,就是空节点,作为链表的开始节点,真正存储数据的节点是第二个节点。这样的话就可以解决删除和添加数据元素到首节点位置时,要改变头指针指向。加了头节点的话删除首节点就和删除其他节点一样,只需要改变数据元素中的指针的指向。
链表元素的插入和删除都是第一个元素位置。这时就不需要
利用for循环去找插入位置前一个元素的地址。因为for循环的时间赋复杂度为n,字节添加和删除首节点位置数据元素的操作时间复杂度为1
#include#include #include //声明结构体 struct node { char name[20]; struct node *prior ,* next; }; //常见链表的头节点 struct node * create() { struct node * head = malloc(sizeof(struct node)); if(head == NULL) { printf("malloc linklist errorn"); return NULL; } head->prior = NULL; head->next = NULL; return head; } //添加数据元素 void insert_list(struct node * head,int pos,char * data) { struct node * p = head; //寻找pos-1的位置地址------------如果选择添加在首节点位置,那么可以不需要写这些 struct node * new = malloc(sizeof(struct node )); new->next = p->next; p->next = new; new->prior = p; strcpy(new->name,data); } //判断是否空间没有存储数据元素 int empty(struct node * head) { if(head->next == NULL) { printf("is emptyn"); return 1; } return 0; } //删除数据元素 void delete_list(struct node * head,int pos) { if(empty(head)) { return ; } struct node * p = head; struct node * q = p->next; p->next = q->next; if(q->next != NULL) { q->next->prior = p; } printf("delete data is : %sn",q->name); free(q); } //修改数据元素------通过查询位置编号 void update_pos_list(struct node * head,int pos ,char * newdata) { struct node * p = head; int i = 0; while(i < pos) { p = p->next; if(p == NULL) { printf("error posn"); return ; } i++; } strcpy(p->name,newdata); } //修改数据元素----------通过查询数据元素内容 void update_data_list(struct node * head,char * data,char * newdata) { struct node * p = head; while(p->next != NULL) { p = p->next; if( strcmp(p->name,data) == 0 ) { strcpy(p->name,newdata); } } } // void show_list(struct node * head) { struct node * p = head; while(p->next != NULL) { p = p->next; printf("%st",p->name); } printf("n"); } int main() { struct node * head = create(); delete_list(head,5); insert_list(head,10,"zhangsan"); insert_list(head,10,"laoliu"); insert_list(head,1,"wangwu"); insert_list(head,1,"laozhang"); show_list(head); delete_list(head,5); show_list(head); delete_list(head,5); show_list(head); }
#include循环链表 循环链表的概念#include int m=0; struct node { int date; struct node * next; }; struct node * creat() { struct node * p = malloc(sizeof(struct node)); if(p == NULL) { printf("申请空间失败n"); return NULL; //可以写return head; 吗? } p->next = NULL; return p; } void add_list(struct node * head,int pos,int new_date) { struct node * p = head; int i=0; while(i next != NULL) { p = p->next; i++; } struct node * new = malloc(sizeof(struct node )); new->next = p->next; p->next = new; new->date = new_date; m++; printf("m=%dn",m); return ; } void show_list(struct node * head) { if(head->next == NULL) { printf("没有元素,无法显示n"); return ; } struct node * p = head->next; while(p != NULL) { printf("%d ",p->date); p = p->next; } putchar('n'); return ; } //判断空间为空 int empty(struct node * head) { if(head->next == NULL) { printf("空间为空n"); return 1; } return 0; } //从小到大排序 void paixu(struct node * head) { if(empty(head)) { return ; } struct node * p = head->next; if(p->next == NULL) { printf("只有一个元素n"); printf("%dn",p->date); return ; } for(int i=0;i for(int j=0;j if(p->date > p->next->date) { int a=0; a = p->date; p->date = p->next->date; p->next->date = a; } p = p->next; //printf("此时p指向的地址里面的值为%dn",p->date); } p=head->next; } } int main() { struct node * head = creat(); show_list(head); for(int i=0;i<10;i++) { int pos = 0,date = 0; printf("请输入需要添加的位置和数据n"); scanf("%d%d",&pos,&date); add_list(head,pos,date); } show_list(head); paixu(head); show_list(head); }
最后一个数据元素指向的地址为头节点的地址
if(p->next == NULL) { p->next = head; } //判断空 if(head->next == head) { } //修改头节点的next和prior指向就行 struct node * creat() { struct node * head = malloc(sizeof(struct node)); //就不存在指针指向空的情况 head -> next = head; head -> prior = head; } //以后在判断最后一个数据元素的时候只需要判断p->next是否为head,不在是判断为NULL链表和顺序表的优缺点 线性表优点
-
连续空间存储数据元素,可以通过地址来表示逻辑关系
-
查看和修改数据元素的时候方便,只需要查看下标就行。
//例如查看下标为pos这个元素 p->nos[pos],(前提是pos< p->num-1); //修改下标为pos这个元素 p->nos[pos] = date;
- 增加和删除元素,时间复杂度很高,O(n)。原因:
- 1 添加一个元素到pos这个位置,需要利用循环,从p->nos[p->num-1] 这个元素开始,直到p->nos[p->pos]为止,将每一个元素向后移动一位才行。
- 1同理删除pos这个位置的元素,需要利用循环,从p->nos[p->pos+1]这个元素开始,到p->nos[p->num-1]这个元素为止,将每一个元素向前移动一位
-
添加和删除元素方便,时间复杂度为O(1)
//添加一个元素------------默认是头插法,就是每次都是从头结点之后插入元素 struct node * p = head; new->next = p->next; p->next = new; //删除一个元素----------头删法 struct node * p = head; struct node *q = p->next; p->next = q->next;
-
动态申请空间,不需要空间连续,元素多少可以随时变化,要多少空间就开辟多少空间
-
修改和查询元素时间复杂度O(n)。需要利用循环从头开始取寻找需要修改或者查询的位置。
//修改元素 struct node * p = head; while(p->next != NULL) { p=p->next; p->date = new_date; }
栈就是一个只允许固定位置操作的线性表,允许操作的一端叫做栈顶,不允许操作的一端叫做栈底。遵循先进后出的原则。
每当操作(删除或者插入)一个栈顶元素,就会有一个新的栈顶元素。
顺序栈逻辑结构: 栈
存储结构:顺序存储
顺序栈的概念开辟一个连续的内存空间,规定一端为不可操作的栈底,将栈顶指向栈底的位置,这样添加数据元素的时候任然满足在栈顶操作,之后每添加一个元素,栈顶就加一,始终保持栈顶指向下一个将要添加的位置。如果想要删除操作,那么只需要将栈顶减1,因为此时的栈顶是指向下一个将要添加元素的位置,而且在栈顶减1后,栈顶指向被删除的元素的地址,下一次如果是添加元素,那么就会添加在当前栈顶的位置,也就是要被删除的元素的地址,这样添加的元素就会覆盖掉之前的要被删除的元素;如果是删除元素,任然需要先将栈顶地址减1,此时栈顶位置已经变了,不会在指向上一次要删除的元素的地址。
#include链式栈#include struct node { int date[10]; int top; //这里是声明一个栈顶指针top,由于数组名加上下标(date+top)就是元素地址,所以没有用int * top的形式 }; //创建一个空栈,里面可以存10个int类型的数 struct node * creat() { struct node * p = (struct node *)malloc(sizeof(struct node)); p->top = 0; return p; } int full(struct node *p) { if(p->top ==(sizeof(p->date) / sizeof(int))) { printf("栈空间已满n"); return 1; } return 0; } //判断栈是否为空 int empty(struct node *p) { if(p->top == 0) { printf("栈空间为空n"); return 1; } return 0; } //从栈顶添加元素 void add_stack(struct node * p,int date) { if(full(p)) { return ; } p->date[p->top] = date; p->top ++; } //从栈顶出栈 void pop_stack(struct node * p) { if(empty(p)) { return ; } p->top --; printf("需要删除的元素为%dn",p->date[p->top]); } //显示栈顶 void show_top(struct node *p) { printf("栈顶存放的元素是:%dn",p->date[p->top -1]); return ; } //遍历栈中所有元素 void show_stack(struct node * p) { int i = p->top -1; while(i >= 0) { printf("%d ",p->date[i]); i--; } putchar('n'); } int main() { struct node * p = creat(); add_stack(p,10); add_stack(p,20); add_stack(p,30); add_stack(p,40); add_stack(p,50); show_top(p); show_stack(p); pop_stack(p); show_stack(p); }
链式栈的逻辑结构为:栈
存储结构为:链式存储
链式栈的栈顶在头节点之后的第一个元素位置,也就是head ->next
#include#include #include //声明结构体 struct node { int date; struct node * next; }; //创建空栈------头节点 struct node * creat() { struct node * head = (struct node * )malloc(sizeof(struct node)); if(head == NULL) { printf("空间申请失败n"); return NULL; } head->next = NULL; return head; } //在栈顶添加元素-------------就是在head->next 指向的地址处进行操作 void add_stack(struct node * head,int date) { struct node * new = (struct node *)malloc(sizeof(struct node)); new->next = head->next; head->next = new; new->date = date; } //判断栈空间是否为空 int empty(struct node *head) { if(head->next == NULL) { printf("栈空间为空,无法删除n"); return 1; } return 0; } //删除元素 void pop_stack(struct node * head) { if(empty(head)) { return ; } struct node * q = head->next; head->next = q->next; free(q); } //遍历栈空间元素 void show_stack(struct node * head) { struct node * p = head->next; while(p->next != NULL) { printf("%d ",p->date); p = p->next; } putchar('n'); } int main() { struct node *head = creat(); pop_stack(head); add_stack(head,10); add_stack(head,20); add_stack(head,30); add_stack(head,40); add_stack(head,50); show_stack(head); pop_stack(head); show_stack(head); }
判断括号匹配问题 -------方式一:利用链式栈
#include#include struct node { char date; struct node *next; }; int main() { char str[10]; int i=0; int num=0; printf("请输入:n"); scanf("%s",str); printf("输入的结果%sn",str); while(str[i] != ' ') { if(str[i]!='[' || str[i]!='(' || str[i]!='<' || str[i]!='{' ) { num ++; } if(str[i]!=']' || str[i]!='}' || str[i]!='>' || str[i]!=')') { num ++; } i++; } if(num!= 0) { printf("不匹配n"); return 0; } struct node * p = malloc(sizeof(struct node )); p->next = NULL; for(int i = 0;i < str[i] != ' ';i++ ) { if(str[i] == '[' || str[i] == '{' || str[i] == '<' || str[i] =='(') { struct node *new = malloc(sizeof(struct node)); new->next = p->next ; p->next = new; new->date = str[i]; printf("添加%cn",new->date); continue; } else if(str[i] -1 != p->next->date && str[i] -2 != p->next->date ) { printf("不匹配n"); //printf("栈顶的元素%cn",p->next->date); break; }else if(p->next->date == '{' && str[i] == '}') //配对{} { struct node * q = p->next; p->next = q->next; printf("%c出栈n",q->date); free(q); if(p->next == NULL) { printf("元素全部出栈,匹配成功n"); break; } continue; } //匹对[] else if(p->next->date == '[' && str[i] == ']') { struct node * q = p->next; p->next = q->next; printf("%c出栈n",q->date); free(q); if(p->next == NULL) { printf("元素全部出栈,匹配成功n"); break; } continue; } //匹对() else if(p->next->date == '(' && str[i] == ')') { struct node * q = p->next; p->next = q->next; printf("%c出栈n",q->date); free(q); if(p->next == NULL) { printf("元素全部出栈,匹配成功n"); break; } continue; } //匹对<> else if(p->next->date == '<' && str[i] == '>') { struct node * q = p->next; p->next = q->next; printf("%c出栈n",q->date); free(q); if(p->next == NULL) { printf("元素全部出栈,匹配成功n"); break; } continue; } } }
判断括号匹配问题 -------方式二:利用顺序栈
#include入栈出栈顺序#include struct stack { int top; char str[10]; }; int main() { char ch[10] ; struct stack * p = malloc(sizeof(struct stack)); p->top = 0; printf("输入符号n"); scanf("%s",p->str); for(int i=0;ch[i] != ' ';i++) { if(p->str[p->top]!= ']' && p->str[p->top] != '}' && p->str[p->top] != ')' && p->str[p->top] != '>') { p->str[p->top] = ch[i]; p->top ++; }else { p->top --; if(ch[i]-1 != p->str[p->top] && ch[i]-2 != p->str[p->top]) { printf("不匹配n"); break; } } } }
只能对栈顶进行操作。一组数据按照顺序入栈,但是出栈可以随时,就是说还有其他数据没有入栈,在栈中的数据就可以出栈。出栈n个数据之后继续入栈其他元素,中途又可以出栈。这样的话,最终出栈得到的元素序列就不定
队列一种特殊的线性表,只允许在线性表两端进行操作,插入端称为队尾,删除端叫做队头。满足先进先出原则
链队列逻辑结构为队列(线性表)
存储结构为链式存储--------------插入端称为队尾,删除端叫做队头。有一个对头指针和一个队尾指针,对头指针指向头节点,队尾指针指向需要添加的元素的前一个元素的地址。
在空链队列是,对头和队尾指针都指向头节点。之后每 添加一个元素都需要将队尾指针指向新添加的元素的地址。
三、树的基本操作 树相关的概念树是一种描述数据和数据之间的关系、存储、运算的数据结构。是节点数大于等于0的有限节点的集合,树只有一个根节点,其他的节点被分为互不相交的有限个集合,这些集合本身也是一颗树,这些树都是根的子树。
树描述的关系是一种层次关系,或者叫做从属关系
树有层次,最大的层叫做树的层
结点的子结点叫做该节点的度,整个树中最大的节点的度叫做树的度,度为0的节点叫做终端节点
每一个节点最多只有两个子节点的树
树中的每个元素都叫做结点
结点的度:一个结点包含的子树的个数
叶子结点(终端结点):度为0的结点
子结点:结点有度,有子树,子树的根节点就是这个结点子结点父结点
兄弟结点:同属于一个父结点
树的度:在树中,最大结点的度,称为整棵树的度
结点的层次:从整棵树的根结点开始,叫做第一层,根的子结点就是第二层,依次加1
树的深度:树中结点层次最大的叫做树的深度
路径:从一个结点到另一个结点,经过的位置叫做路径
路径的边:
路径长度:经过的边的条数叫做路径长度
有序树:(兄弟结点不能交换顺序)根结点的子树不能交换
顺序树:就是一种一对多的关系,结点只有一个前驱,可以有多个后继
树的存储链式存储:设计每个结点按照树的度设计指针空间(关系),来存储下一层的子结点的地址
二叉树的概念对于树而言,存储过于繁琐,操作过于复杂,所以想设计一种简单的树来进行表示操作存储。只需要把普通的树转换为这种简单的二叉树就可以了。
二叉树树中的每个结点最多只有两个子树(两个子结点),且严格区分左右子树,不能交换
-
二叉树的第i层,最多有2^(i-1)个结点
-
深度为k的二叉树,最多有2^k - 1 个结点
-
对于二叉树,叶子结点数n0,度为二的结点数n2,n2 = n0 - 1
-
满二叉树:每层都是2^(i-1)个结点,每层都是最多结点
-
完全二叉树:如果有k层,从第一层到k-1层,每一场都是2^(k-1),第k层,一定是从左至右每个位置都有结点(不一定满),也不一定是必须一左一右两个终端节点,也可以是一个左,但是这个节点就没有兄弟节点了,因为已经空了右节点。
在二叉树中,从下标为1开始存储,根结点存储下标为1位置,
当前结点的左孩子存储 2 * i(i就是结点的下标),
结点右孩子存储在 2 * i + 1位置
父节点:当前结点的位置 / 2 ,就是父结点的位置
如果i从0开始,那么
当前结点的左孩子存储 2 * (i+1)-1==2i+1(i就是结点的下标),
结点右孩子存储在 2 * (i + 1) +1 -1 == 2i +2位置
父节点:当前结点的位置 / 2 ,就是父结点的位置
二叉树的遍历- 顺序栈遍历:先将根节点遍历根节点(根节点出栈),把它左节点入栈,然后右节点入栈,之后右节点处于栈顶,先对器进行出栈,同理将有节点的左右子节点入栈,然后遍历右子节点(出栈),后续同上操作。直到所有节点全部遍历完(出栈)。
- **顺序队列遍历(层次遍历):**先遍历根节点(根节点出栈),
- 先序遍历:根------左子树---------右子树
- 中序遍历:左子树----根-----右子树
- 后序遍历:左子树-------右子树------根
利用先序+中序 或者后序+中序的方式可以还原二叉树结构,得到另外一个遍历顺序的结果
具体方法是:
- 利用先序的第一个元素就是根节点,找到根节点。
- 然后再中序中判断其余元素是根的左子树还是由子树成员
- 继续看先序中的下一个元素,并在中序中找到该元素,然后以它为根,利用左–根–右的顺序判断该元素两边的元素在该元素的左边还是右边
- 继续看先序中的下一个元素,,并在中序中找到该元素,然后以它为根,利用左–根–右的顺序判断该元素两边的元素在该元素的左边还是右边
- 直到全部元素遍历完
在树中,权值(比重)大的数据放在二叉树的最上层,权值小的放在二叉树的最下层。
带权路径长度:节点的权值 * 根节点到该节点的边数
带权路径长度就可以反应某个节点访问的难以程度
所有要访问的数据都是叶子节点(就是度为0的节点),其余节点就是这个树所占的权重。
四、图的概念 图的存储邻接矩阵
邻接链表
计数排序:利用待排序元素中最大的元素的值m,来创建一个m+1这么大的一个数组b,然后按顺序将待排序的元素的值,当做b数组的下标,找到该下标对应的元素,对其加1。如果在待排序元素中一个数出现n次,那么就需要将b数组中该元素值为下标的数加n。待排序元素中没有的数,在数组b中就是0;所有带排序元素都遍历完后,将数组b遍历。利用for循环判断条件是b[ i ] != 0,就打印下标 i, 之后b[ i ] - - ,直到不满足条件
#include选择排序int main() { int a[5] = {4,2,6,4,90}; int max=0; for(int i = 0; i< sizeof(a) / sizeof(int) ;i++) { if(a[i] > max) { max = a[i]; } } printf("max=%dn",max); int n=max+1; int b[n]; bzero(b,n*4); //关键之处,不能将int b[n];写成int b[n]就不管了,因为此时数组大小是个未知数,这种定义方式无异于int b[];这种方式,都是不允许的。需要利用bzero()函数加数组中的所有元素都赋值为0 for(int i = 0; i < sizeof(a) / sizeof(int); i++) { if(a[i] >= 0) { b[a[i]]++; } } for(int i = 0; i while(b[i] != 0) { printf("%d ",i); b[i]--; } } putchar('n'); }
一种与冒泡排序很像的排序。他是将第一个元素和该元素之后的每一个元素进行比较,如果被比较的元素大于(小于)比较的元素,那么就交换值的大小,之后被比较元素继续用新的值和之后的元素比较,直到它和所有元素都比完。此时被比较元素一定是所有比较元素中最小的。之后利用该方法,取出第二个元素,然它和它之后的每一一个元素比较,方法一样。
和冒泡拍序不同之处在于,里层循环的条件是从i+1开始,小于带比较元素的个数
#include冒泡排序int main() { int a[5] = {1,4,6,3,7}; for(int i = 0; i < sizeof(a)/sizeof(int ) - 1; i++) { for(int j = i+1; j if(a[i] > a[j]) { int b = a[i]; a[i] = a[j]; a[j] = b; } } } for(int i = 0;i < sizeof(a) / sizeof(int);i++) { printf("%d ",a[i]); } putchar('n'); }
从第零个元素开始,将前一个元素和它后面一个元素进行比较,如果前一个元素大于后一个元素,那么交换位置(交换值),依次类推,直到倒数第二个元素和倒数第一个元素比较完,此时最后一个元素一定是所有元素中最大的。之后在进行第二轮,从第零个元素开始,前一个元素和它后面一个元素进行比较,如果前一个元素大于后一个元素,那么交换位置(交换值),依次类推,直到倒数第二个元素和倒数第一个元素比较完(为了减小时间复杂度,需要减i
),此时最后一个元素一定是所有元素中最大的
#include插入排序int main() { int a[5] = {2,6,3,5,1}; for(int i =0;i for(int j = 0;j < sizeof(a) / sizeof(int) -1 -i; j++) { if(a[j] > a[j+1]) { int b = a[j]; a[j] = a[j+1]; a[j+1] = b; } } } for(int i = 0; i < sizeof(a) / sizeof(int) ; i++) { printf("%d ",a[i]); } putchar('n'); }
#include折半查找int main() { int a[5] = {2,1,4,23,5}; for(int i = 1;i < sizeof(a) / sizeof(int );i++) { int temp = a[i]; int j = i; while(j >0) { if(a[j-1] > temp) { a[j] = a[j-1]; }else { break; } j--; } a[j] = temp; } for(int i=0 ; i printf("%d ",a[i]); } putchar('n'); }
#includeint main() { int a[10] = {1,4,6,2,7,3,8,4,5,3}; int i = 0,j = 9; int mid = (i + j) / 2; int num =0; scanf("%d",&num); while(i <= j) { if(num == a[mid]) { printf("找到,位置为%dn",mid); break; } else if(num > a[mid]) { i = mid +1; }else { j = mid -1; } mid = (i + j) / 2; } }