408-Data Structure

6.24代码不知道何用

#include<stdio.h>
#include<stdlib.h>
#define InitSize 10 //默认的最大长度
typedef struct { //指示动态分配数组的指针
    int *data;
    int MaxSize;//顺序表的最大容量
    int length;//顺序表的当前长度
} SeqList,*List;

void InitList(List &L) {
//用 malloc 函数申请一片连续的存储空间
    L->data=(int *)malloc(InitSize*sizeof(int));
    L->length=3;
    L->data[0]=1;
    L->data[1]=2;
    L->data[2]=3;
    L->MaxSize=InitSize;
}
int main() {
    List L;
    InitList(L);
    int n;
    scanf("%d",&n);
    int i= InitSize;
    while(i!=1){
        if(L->data[i-1]!=0)
            L->data[i]=L->data[i-1];
        i--;
    }
    L->data[1]=n;
    for (int j = 0; j < InitSize; ++j) {
        if(L->data[j]!=0)
        printf("%d",L->data[j]);
    }
    return 0;
}

一、

(一)

1.算法描述

时间复杂度

时间复杂度三要素,初始条件、结束条件、递增情况

1.其中这里把x=1, x=2都可以

2.结束条件,这道题也就是x一直乘到n/2,但是多乘一次就是n,就直接按照乘2乘多少次到n这样算就可以

3.时间复杂度是一个数量级,不是一个精确数,三要素在计算的时候可以在同数量级下改变

例如n/2和n就是一个数量级

二、线性表

(一)补充

1.指向结构体的指针访问成员变量

指向结构体的指针

☀ 每个结构体变量都有自己的存储空间和地址,因此指针也可以指向结构体变量

☀ 结构体指针变量的定义形式:struct 结构体名称 *指针变量名

☀ 有了指向结构体的指针,那么就有3种访问结构体成员的方式

✎ 结构体变量名.成员名

✎ (*指针变量名).成员名

✎ 指针变量名→ 成员名

输出结果:

2.指针变量和普通变量(malloc的使用)

        一开始怀疑声明指针变量之后,系统并不会分配内存空间,因为在学习这节内容遇到了一个关于malloc的问题,为什么声明完结构体指针不能直接给结构体指针中的data赋值,还需要malloc函数开辟内存空间。

        原因其实很简单,声明完指针变量,系统也会自动分配给结构体指针变量需要的内存空间,只不过这个内存空间存放的是地址信息,需要存放的是同类型结构体变量所在的内存空间地址信息。

        而真正的结构体变量所需要的内存空间,可以通过定义一个同类型的结构体变量来实现,也可以使用malloc函数来实现。

神作:结构体指针 - congmingyige - 博客园 (cnblogs.com)

3.p->next的理解

理解

在结构体中 由数据域、指针域组成

struct node
{
int data; //数据域
struct node * next;//指针域
}node;
3.实例

在数据结构中  线性表的插入(头插法或者尾插法)中通常使用的交换语句

s->next = p->next;
p->next = s;
第一段代码的意思是 :p  指针指向的节点的指针域指向下一个节点的地址 赋值 给 s指针所指向的节点的指针域指向的下一个节点的地址(通俗的说:就是将插入的 节点s 的指针域指向 原先p指针指向的节点的地址)

第二段代码的意思是:将s指针所指向的节点的地址赋值给 p指针所指向的节点(结构体)的指针域的指针域所指向下一个节点的地址(通俗的说:就是将p节点(p指针所指向的节点)的指针域指向s指针所指向的节点) 

 例如以下代码

void list_head_insert(LinkList &L){
    //这里申请的是结构体大小的空间s= (LinkList)malloc(sizeof(LinkList));标准的错误,结构体指针的大小只有八个字节的空间
    L= (LinkList)malloc(sizeof(LNode));//头指针指向头结点
    LNode *s;int x;
    s= (LinkList)malloc(sizeof(LNode));
    scanf("%d",&x);
    s->data=x;
    s->next=NULL;
    L->next=s;//这里的L->next使用的是L所指向的结点的指针域

}
int main() {
    LinkList L;
    list_head_insert(L);
    return 0;
}

(二)代码 

1.顺序表:动态分配存储空间(c)

#include<stdio.h>
#include<stdlib.h>
#define InitSize 10 //默认的最大长度
typedef struct { //指示动态分配数组的指针
	int *data;
	int MaxSize;//顺序表的最大容量
	int length;//顺序表的当前长度
} SeqList;

void InitList(SeqList *L) {
//用 malloc 函数申请一片连续的存储空间
	(*L).data=(int *)malloc(InitSize*sizeof(int));
	(*L).length=0;
	(*L).MaxSize=InitSize;
}

void IncreaseSize(SeqList *L,int len) {
	int *p=(*L).data;
	(*L).data=(int *)malloc(((*L).MaxSize+len)*sizeof(int));
	for(int i=0; i<(*L).length; i++) {
		(*L).data[i]=p[i];
		(*L).MaxSize=(*L).MaxSize+len;
		free(p);
	}
}

int main() {
	SeqList L;
	InitList(&L);
	L.data[0]=1;
	printf("%d",L.data[0]);
	IncreaseSize(&L,5);
	return 0;
}

2.单向链表头插法定义与创建(c++)

王道咸鱼讲的是return null;但是编译报错,需要修改成return NULL;

#include <stdlib.h>
#include <stdio.h>
typedef struct LNode{
    int data;
    struct LNode *next;
}LNode,*LinkList;
void list_head_insert(LinkList &L){
    //这里申请的是结构体大小的空间s= (LinkList)malloc(sizeof(LinkList));标准的错误,结构体指针的大小只有八个字节的空间
    L= (LinkList)malloc(sizeof(LNode));//头指针指向头结点
    L->next=NULL;
    LNode *s;int x;
    scanf("%d",&x);
    while (x!=9999){
        s= (LinkList)malloc(sizeof(LNode));
        s->data=x;
        s->next=L->next;
        L->next=s;
        scanf("%d",&x);
    }
}
void print_list(LinkList L){
    while (L!=NULL){
        L=L->next;
        printf("%3d",L->data);

    }
}
int main() {
    LinkList L;
    list_head_insert(L);
    print_list(L);
    return 0;
}

3.单链表尾插法 

#include <stdlib.h>
#include <stdio.h>
typedef struct LNode{
    int data;
    struct LNode *next;
}LNode,*LinkList;
void list_tail_insert(LinkList &L){
    //这里申请的是结构体大小的空间s= (LinkList)malloc(sizeof(LinkList));标准的错误,结构体指针的大小只有八个字节的空间
    L= (LinkList)malloc(sizeof(LNode));//头指针指向头结点
    L->next=NULL;
    LNode *s,*r;int x;//r尾指针永远指向尾结点
    r=L;//一开始和头指针一样指向头结点
    scanf("%d",&x);
    while (x!=9999){
        s= (LinkList)malloc(sizeof(LNode));
        s->data=x;
        r->next=s;
        r=r->next;//也可以写成r=s;
        scanf("%d",&x);
    }
    r->next=NULL;
}
void print_list(LinkList L){
    while (L!=NULL){
        L=L->next;
        printf("%3d",L->data);

    }
}
int main() {
    LinkList L;
    list_tail_insert(L);
    print_list(L);
    return 0;
}

4.单链表按位插入带头结点

关键要让结点指针指向要插入的位置的前一个结点,这样无论要插入那个位置就好插入了,带头结点的话指针永远指向头结点,但是没有头结点的话,有时候要改变头指针的值
 

#include <stdlib.h>
#include <stdio.h>
typedef struct LNode{
    int data;
    struct LNode *next;
}LNode,*LinkList;
void list_tail_insert(LinkList &L){
    //这里申请的是结构体大小的空间s= (LinkList)malloc(sizeof(LinkList));标准的错误,结构体指针的大小只有八个字节的空间
    L= (LinkList)malloc(sizeof(LNode));//头指针指向头结点
    L->next=NULL;
    LNode *s,*r;int x;//r尾指针永远指向尾结点
    r=L;//一开始和头指针一样指向头结点
    scanf("%d",&x);
    while (x!=9999){
        s= (LinkList)malloc(sizeof(LNode));
        s->data=x;
        r->next=s;
        r=r->next;//也可以写成r=s;
        scanf("%d",&x);
    }
    r->next=NULL;
}
bool list_insert(LinkList &L,int i,int e){
    if(L==NULL&&i<1)//链表的有效结点编号是从1开始的
        return false;
    LNode *p,*s;
    int j=0;//p指针当前所指向的是第几个结点
    p=L;
    while (p!=NULL&&j<i-1){//按位插入,若要插入的位置是2那么就需要让p指针指向第一个结点
        p=p->next;
        j++;
    }
    if(p==NULL)
        return false;
    s=(LinkList) malloc(sizeof (LNode));
    s->data=e;
    s->next=p->next;
    p->next=s;
    return true;
}
void print_list(LinkList L){
    while (L!=NULL){
        L=L->next;
        printf("%3d",L->data);

    }
}
int main() {
    LinkList L;
    list_tail_insert(L);
    list_insert(L,2,99);
    print_list(L);
    return 0;
}

5.单链表按位序查找

#include <stdlib.h>
#include <stdio.h>
typedef struct LNode{
    int data;
    struct LNode *next;
}LNode,*LinkList;
void list_tail_insert(LinkList &L){
    //这里申请的是结构体大小的空间s= (LinkList)malloc(sizeof(LinkList));标准的错误,结构体指针的大小只有八个字节的空间
    L= (LinkList)malloc(sizeof(LNode));//头指针指向头结点
    L->next=NULL;
    LNode *s,*r;int x;//r尾指针永远指向尾结点
    r=L;//一开始和头指针一样指向头结点
    scanf("%d",&x);
    while (x!=9999){
        s= (LinkList)malloc(sizeof(LNode));
        s->data=x;
        r->next=s;
        r=r->next;//也可以写成r=s;
        scanf("%d",&x);
    }
    r->next=NULL;
}
bool list_insert(LinkList &L,int i,int e){
    if(L==NULL&&i<1)//链表的有效结点编号是从1开始的
        return false;
    LNode *p,*s;
    int j=0;//p指针当前所指向的是第几个结点
    p=L;
    while (p!=NULL&&j<i-1){//按位插入,若要插入的位置是2那么就需要让p指针指向第一个结点
        p=p->next;
        j++;
    }
    if(p==NULL)
        return false;
    s=(LinkList) malloc(sizeof (LNode));
    s->data=e;
    s->next=p->next;
    p->next=s;
    return true;
}
void print_list(LinkList L){
    while (L!=NULL){
        L=L->next;
        printf("%3d",L->data);

    }
}
LinkList GetElem(LinkList L,int i){
    int j=0;
    while (L!=NULL&&j<i){
        L=L->next;
        j++;
    }
    return L;
}
int main() {
    LinkList L,search;
    list_tail_insert(L);
//    list_insert(L,2,99);
    search= GetElem(L,2);
//    print_list(search);
    printf("%d",search->data);
    return 0;
}

6.单链表插入,是根据GetElem找到指向所要插入的位置的前一个结点的指针,直接根据这个指针进行操作

#include <stdlib.h>
#include <stdio.h>
typedef struct LNode{
    int data;
    struct LNode *next;
}LNode,*LinkList;
void list_tail_insert(LinkList &L){
    //这里申请的是结构体大小的空间s= (LinkList)malloc(sizeof(LinkList));标准的错误,结构体指针的大小只有八个字节的空间
    L= (LinkList)malloc(sizeof(LNode));//头指针指向头结点
    L->next=NULL;
    LNode *s,*r;int x;//r尾指针永远指向尾结点
    r=L;//一开始和头指针一样指向头结点
    scanf("%d",&x);
    while (x!=9999){
        s= (LinkList)malloc(sizeof(LNode));
        s->data=x;
        r->next=s;
        r=r->next;//也可以写成r=s;
        scanf("%d",&x);
    }
    r->next=NULL;
}
bool list_insert(LinkList &L,int i,int e){
    if(L==NULL&&i<1)//链表的有效结点编号是从1开始的
        return false;
    LNode *p,*s;
    int j=0;//p指针当前所指向的是第几个结点
    p=L;
    while (p!=NULL&&j<i-1){//按位插入,若要插入的位置是2那么就需要让p指针指向第一个结点
        p=p->next;
        j++;
    }
    if(p==NULL)
        return false;
    s=(LinkList) malloc(sizeof (LNode));
    s->data=e;
    s->next=p->next;
    p->next=s;
    return true;
}
bool list_getelem_insert(LNode *p,int e){
    if(p==NULL){
        return false;
    }
    LNode *s;
    s= (LinkList)malloc(sizeof (LNode));
    s->data=e;
    s->next=p->next;
    p->next=s;
    return true;
}
void print_list(LinkList L){
    while (L!=NULL){
        L=L->next;
        printf("%3d",L->data);

    }
}
LinkList GetElem(LinkList L,int i){
    int j=0;
    while (L!=NULL&&j<i){
        L=L->next;
        j++;
    }
    return L;
}
int main() {
    LinkList L,search;
    list_tail_insert(L);
//    list_insert(L,2,99);
    search= GetElem(L,2);
    printf("%d",search->data);
//    list_insert(L,2,99);遍历到所要插入的位置的前一个结点再插入
    list_getelem_insert(search,999);//通过getelem找到指向所要插入的位置的前一个结点的指针
    print_list(L);
    return 0;
}

7.双链表(缺失部分功能,主要思想都有)

#include <iostream>
typedef struct DNode{
    int data;
    struct DNode *prior,*next;
}DNode,*DLinkList;
void init_dlinklist(DLinkList &d){
    d= (DLinkList)malloc(sizeof (DNode));
    d->next=NULL;
    d->prior=NULL;
}
//将要插入的新结点s,插入到p结点之后
bool insert_next_node(DNode *p,DNode *s){
    s->next=p->next;
    if(p->next!=NULL)
        p->next->prior=s;
    s->prior=p;
    p->next=s;
    return true;
}
//删除p结点的后继结点
bool delete_next_dnode(DNode *p){
    DNode *s;
    s=p->next;
    if(p->next==NULL)
        return false;
    p->next=s->next;
    if (s->next!=NULL)
        s->next->prior=p;
    free(s);
    return true;
}
int main() {
    DLinkList d;
    init_dlinklist(d);
    return 0;
}

8.循环链表 

9.单链表真题实战

#include <stdlib.h>
#include <stdio.h>
typedef struct LNode{
    int data;
    struct LNode *next;
}LNode,*LinkList;
void list_tail_insert(LinkList &L){
    //这里申请的是结构体大小的空间s= (LinkList)malloc(sizeof(LinkList));标准的错误,结构体指针的大小只有八个字节的空间
    L= (LinkList)malloc(sizeof(LNode));//头指针指向头结点
    L->next=NULL;
    LNode *s,*r;int x;//r尾指针永远指向尾结点
    r=L;//一开始和头指针一样指向头结点
    scanf("%d",&x);
    while (x!=9999){
        s= (LinkList)malloc(sizeof(LNode));
        s->data=x;
        r->next=s;
        r=r->next;//也可以写成r=s;
        scanf("%d",&x);
    }
    r->next=NULL;
}
void print_list(LinkList L){//按照原本的输出则只会输出l1 的值l2 的值不会输出为NULL
    L=L->next;
    while (L!=NULL){
        printf("%3d",L->data);
        L=L->next;
    }
}
void find_middle(LinkList L,LinkList &L2) {
    L2 = (LinkList) malloc(sizeof(LNode));
    LNode *ppre, *pcur;
    ppre = pcur = L->next;

    while (pcur) {
        pcur = pcur->next;
        if (pcur == NULL) {
            break;
        }
        pcur = pcur->next;//这不需要判断如果为空while下一次循环不会执行
        if (pcur == NULL) {//为了使偶数个结点的时候ppre指向中间两个结点的前一个
            break;
        }
        ppre=ppre->next;
    }
    L2->next=ppre->next;//双指针遍历法有疑问可查
    ppre->next=NULL;//前一半链表尾为空
}
void reverse(LinkList L){//原地逆置需要三个指针
LinkList r,s,t;
if(L->next==NULL){
    return;
}
r=L->next;
    while (t!=NULL){//前后两个结点逆置
        s=r->next;
        t=s->next;
        s->next=r;
        r=s;
        s=t;
        t=t->next;
    }
    s->next=r;
    L->next->next=NULL;
    L->next=s;
}
void merge(LinkList L1,LinkList L2){
    //pcur永远指向新链表的尾结点
    LinkList pcur,p,q;
    pcur=L1->next;
    p=pcur->next;
    q=L2->next;
    while (p!=NULL&&q!=NULL){
        pcur->next=q;
        pcur=pcur->next;
        q=q->next;
        pcur->next=p;
        p=p->next;
        pcur=pcur->next;
    }
    if(p!=NULL){//遍历完之后这两个链表之中有一个还剩一个结点
        pcur->next=p;
    }
    if(q!=NULL){
        pcur->next=q;
    }

}
int main() {
    LinkList L1;
    LinkList L2;
    L2=NULL;
    list_tail_insert(L1);
    find_middle(L1,L2);//只有一个结点也有效
    printf("\n");
    print_list(L1);
    printf("\n");
    print_list(L2);
    reverse(L2);
    merge(L1,L2);
    printf("\n");
    print_list(L1);
    return 0;
}

合并的时候实现效果是将L2的结点隔一个插入L1链表当中,所以最后输出,只需遍历L1链表即可 

reverse最后几步的效果 

三、栈、队列和数组

(一)

(二)代码

1.手搓栈的链式存储

出栈操作有但没用到

#include <iostream>
typedef struct stack{
    int data;
    struct stack *next;
}nstack,*lstack;

void init_lstack(lstack &s){
    s= (lstack)malloc(sizeof (nstack));//让头指针指向头结点
    s->next=NULL;
}
bool push_lstack(lstack &s,int x){
    nstack *p;//要插入的链结点
    p= (lstack)malloc(sizeof (nstack));
    if (p==NULL){
        return false;
    }
    p->data=x;
    p->next=s->next;
    s->next=p;
    return true;
}
bool pop_lstack(lstack &s){
    nstack *p;
    p=s->next;
    if (s->next!=NULL){
    s->next=p->next;
    free(p);
    }
    return true;
}
void print_lstack(lstack s){
    while (s->next!=NULL){
        s=s->next;
        printf("%d",s->data);
    }
}
void pushStack(lstack &s){
    int x=0;
    scanf("%d",&x);
    while (x!=9999){
        push_lstack(s,x);
        scanf("%d",&x);
    }
}
int main() {
    lstack s;
    init_lstack(s);
    pushStack(s);
    print_lstack(s);
    return 0;
}

 2.顺序栈

#include <stdio.h>
#define MaxSize 10
typedef int ElementType;
typedef struct stack{
    ElementType data[MaxSize];
    int top;
}SqlStack;
void init_stack(SqlStack &s){
    s.top=-1;
}
bool stack_empty(SqlStack s){
    if(s.top==-1)
        return true;
    return false;
}
bool push(SqlStack &s,ElementType e){
    if(s.top==MaxSize-1){
        return false;
    }
    s.top++;
    s.data[s.top]=e;
    return true;
}
bool get_top(SqlStack s,ElementType &m){
    if(stack_empty(s)){
        return false;
    }
    m=s.data[s.top];
    return true;
}
bool pop(SqlStack &s,ElementType &m){//弹栈
    if(stack_empty(s)){
        return false;
    }
    m=s.data[s.top--];
    return true;
}
int main(){
    SqlStack s;
    bool flag;
    init_stack(s);
    flag=stack_empty(s);
    push(s,3);
    push(s,4);
    push(s,5);
    ElementType m;
    get_top(s,m);
    printf("%d",m);
    return 0;

}

 3.循环队列

q.rear=q.front是用来判断循环队列是否为空,但是队列中如果每个位置都存储数据的话,q.rear=q.front也会被满足,因此为了区分判空和判满,在存入数据的时候q.rear和q.front之间至少会空一个位置

查队列就是出队列

#include <stdio.h>
#define MaxSize 5
typedef int ElementType;
typedef struct stack{
    ElementType data[MaxSize];
    int front,rear;
}SqQueue;
void init_Queue(SqQueue &q){
    q.front=q.rear=0;
}
bool queue_empty(SqQueue q){
    return q.rear==q.front;
}
bool EnQueue(SqQueue &q,ElementType e) {//当在q.rear=3的时候插入数据,插入完毕q.rear+1=4,而进入函数判满的时候,此时if条件中成立,所以此时q.rear=4中插入不了数据
    //
    if ((q.rear+1)%MaxSize==q.front)//判断循环队列是否满,使用这种方法最后会空出一个元素,这样做是为了好实现(业界公认)
        return false;
    q.data[q.rear] = e;
    q.rear=(q.rear+1)%MaxSize;//rear+1,如果大于数组长度,回到开头
    return true;
}
bool DeQueue(SqQueue &s,ElementType &m){
    if(s.rear==s.front){
        return false;
    }
    m=s.data[s.front];
    s.front=(s.front+1)%MaxSize;
    return true;
}
int main(){
    SqQueue q;
    init_Queue(q);
    EnQueue(q,1);
    EnQueue(q,2);
    EnQueue(q,3);
    EnQueue(q,4);
    EnQueue(q,5);
    EnQueue(q,6);
    EnQueue(q,7);
    EnQueue(q,8);

    ElementType m;
    m= DeQueue(q,m);
    printf("%d",m);
}

4.循环队列(链表实现) 

 (2)

(3)先判断rear->next是否等于front,如果等于则创建新结点,rear当前指向的结点存数据,rear=rear->next,也就是当前rear指针指向的结点data还是为空,头结点是要存放数据的 

(4) 

4.强化栈

#include <stdio.h>
#include <cstdlib>

#define MaxSize 10
typedef int ElementType;
typedef struct stack{
    ElementType data;
    struct stack *next;
    struct stack *pre;
    int length;//单独用一个变量来记录链表长度,用于判满
}*Sqlstack,sqln;
typedef struct stackdouble{
    struct stack *rear;//实现双向链表并且栈顶在链尾部,虚假尾指针
}stackdouble;

void init_stack(Sqlstack &s,stackdouble &d){
    s=(sqln *)malloc(sizeof(sqln));
    s->data=-1;
    d.rear=s;
}

bool emptystack(Sqlstack &s){
    if(s->length==0){
        return true;
    }
    return false;
}

bool push(Sqlstack &s,int x,stackdouble &d){
    if(s->length<MaxSize&&s->data!=-1){
        sqln *n;
        n=(sqln*) malloc(sizeof (sqln));
        n->data=x;
        n->pre=d.rear;
        d.rear->next=n;
        d.rear=n;
        n->next=NULL;
        return true;
    }
    return false;
}
//尾指针可以通过前驱指针指向当前结点前驱结点,实现出栈
bool pop(Sqlstack &s,ElementType &x,stackdouble &d){
    if(s->data!=-1){
        Sqlstack p;
        p=d.rear;
        x=p->data;
        d.rear=d.rear->pre;
        d.rear->next=NULL;
        free(p);
        return true;
    }
    return false;
}
int main(){

}

​​​​​​​

#include <stdio.h>
#include <cstdlib>

#define MaxSize 10
typedef int ElementType;
typedef struct stack{
    ElementType data;
    struct stack *next;
    int length;//单独用一个变量来记录链表长度,用于判满
}*Sqlstack,sqln;

void init_stack(Sqlstack &s){
    s=(sqln *)malloc(sizeof(sqln));
    s->data=-1;
}

bool emptystack(Sqlstack &s){
    if(s->length==0){
        return true;
    }
    return false;
}

bool push(Sqlstack &s,int x){
    if(s->length<MaxSize&&s->data!=-1){
        sqln *n;
        n=(sqln*) malloc(sizeof (sqln));
        n->data=x;
        n->next=s->next;
        s->next=n;
        s->length=s->length+1;
        return true;
    }
    return false;
}

bool pop(Sqlstack &s,ElementType &x){
    if(s->data!=-1){
        Sqlstack p;
        p=s->next;
        x=p->data;
        s->next=p->next;
        free(p);
        return true;
    }
    return false;
}
int main(){

}
#include <stdio.h>
#define MaxSize 10
typedef int ElementType;
typedef struct stack{
    ElementType a[MaxSize];
    int top;
}Sqlstack;

void init_stack(Sqlstack &s){
    s.top=-1;
}

bool emptystack(Sqlstack &s){
    if(s.top==-1){
        return true;
    }
    return false;
}

bool push(Sqlstack &s,int x){
    if(s.top<MaxSize){
        s.a[++s.top]=x;
        return true;
    }
    return false;
}

bool pop(Sqlstack &s,ElementType &x){
    if(s.top!=-1){
        x=s.a[s.top--];
        return true;
    }
    return false;
}
int main(){

}

四、树

(一)代码

1.层次建树、前中后、层次遍历

①层次建树:

结点a:如果队列为空则把结点放到队列,并把当前结点作为根结点pcur,ptail,phead全部指向a结点。

结点b:入队,ptail指向b,判断pcur当前指向的结点(a)是否有左孩子,没有,左孩子为b

结点c:入队,ptail指向c,判断pcur当前指向的结点(a)是否有左孩子,有,是否有右孩子,没有,右孩子为b,pcur指向队列中下一个结点(b)

#include <stdio.h>
#include <stdlib.h>
typedef char BiElemType;
typedef struct BiTNode{
BiElemType c;//c 就是书籍上的 data
struct BiTNode *lchild;
struct BiTNode *rchild;
}BiTNode,*BiTree;
//tag 结构体是辅助队列使用的
typedef struct tag{
BiTree p;//树的某一个结点的地址值
struct tag *pnext;
}tag_t,*ptag_t;


//队列的相关数据结构
typedef BiTree ElemType;
typedef struct LinkNode{
    ElemType data;
    struct LinkNode *next;
}LinkNode;
typedef struct{
    LinkNode *front,*rear;
}LinkQueue;

void InitQueue(LinkQueue &Q){
    Q.front=Q.rear=(LinkNode*) malloc(sizeof (LinkNode));//头和尾指向同一个结点
    Q.front->next=NULL;//头结点next指针为NULL
}

bool IsEmpty(LinkQueue Q){
    if(Q.rear==Q.front){
        return true;
    }
    return false;
}
//入队,尾部插入法
void EnQueue(LinkQueue &Q,ElemType x)
{
    LinkNode *s=(LinkNode *)malloc(sizeof(LinkNode));
    s->data=x;s->next=NULL;
    Q.rear->next=s;//rear 始终指向尾部
    Q.rear=s;
}
//出队头部删除法
bool DeQueue(LinkQueue &Q,ElemType &x)
{
    if(Q.front==Q.rear) return false;//队列为空
    LinkNode *p=Q.front->next;//头结点什么都没存,所以头结点的下一个节点才有数据
    x=p->data;
    Q.front->next=p->next;//断链
    if(Q.rear==p)//删除的是最后一个元素
        Q.rear=Q.front;//队列置为空
    free(p);
    return true;
}


//递归实现
//abdhiejcfg 前序遍历,前序遍历就是深度优先遍历
void PreOrder(BiTree p) {
    if (p != NULL) {
        putchar(p->c);//等价于 visit 函数
        PreOrder(p->lchild);
        PreOrder(p->rchild);
    }
}
//中序遍历
void InOrder(BiTree p) {
    if (p != NULL) {
        InOrder(p->rchild);
        putchar(p->c);//等价于 visit 函数
        InOrder(p->lchild);
    }
}
//后序遍历
void PostOrder(BiTree p) {
    if (p != NULL) {
        PostOrder(p->rchild);
        PostOrder(p->lchild);
        putchar(p->c);//等价于 visit 函数
    }
}

//层次遍历,层序遍历,广度优先遍历
void LevelOrder(BiTree T) {
    LinkQueue Q;//辅助队列
    InitQueue(Q);//初始化队列
    BiTree p;
    EnQueue(Q, T);//树根入队
    while (!IsEmpty(Q)) {
        DeQueue(Q, p);//出队当前结点并打印
        putchar(p->c);
        if (p->lchild != NULL) {//入队左孩子
            EnQueue(Q, p->lchild);
        }
        if (p->rchild != NULL) {//入队左孩子
            EnQueue(Q, p->rchild);
        }
    }
}

int main(){
    BiTree pnew;
    BiTree tree;
    tree=NULL;//shu gen
    char c;
    ptag_t phead=NULL,pcur=NULL,ptail=NULL,listpnew=NULL;
    while(scanf("%c",&c)){
        if(c=='\n'){//不能用双引号
            break;
        }
        pnew=(BiTree) calloc(1,sizeof (BiTNode));//申请空间并对空间初始化,赋值为0
        pnew->c=c;
        listpnew=(ptag_t) calloc(1,sizeof (tag_t));//给队列申请空间
        listpnew->p=pnew;
        if(tree==NULL){
            tree=pnew;
            phead=listpnew;
            ptail=listpnew;
            pcur=listpnew;
            continue;
        }else{
            ptail->pnext=listpnew;
            ptail=ptail->pnext;
            //将b结点放入述中
        }//pcur始终指向要插入结点的父亲结点
        //开始插入b结点
        if(pcur->p->lchild==NULL){
            pcur->p->lchild=ptail->p;
        }else if(pcur->p->rchild==NULL){
            pcur->p->rchild=ptail->p;
            pcur=pcur->pnext;//左右都放了结点,处理下一个父亲结点
        }
    }
    printf("--------前序遍历----------\n");//也叫先序遍历,先打印当前结点,打印左孩子,打印右孩子
    PreOrder(tree);
    printf("\n--------中序遍历------------\n");//先打印左孩子,打印父亲,打印右孩子
    InOrder(tree);
    printf("\n--------后序遍历------------\n");//先打印左孩子,打印右孩子,最后打印父亲
    PostOrder(tree);
    printf("\n--------level遍历------------\n");//先打印左孩子,打印右孩子,最后打印父亲
    LevelOrder(tree);
    return 0;
}

(二)章节内容

1.树的定义和基本术语

前驱、后继、叶子、分支结点

树中有前驱和后继的概念

边由称为分支,结点用来存放数据

树的基本概念、如何判断是否为树 

 任何一个树都可以被看作是一个根节点和若干个不相交的子树组成的

 祖先、双亲、路径、路径长度

层次、高度、结点的度、树的度 
 有序树无序树

有没有序主要看存放的数据之间有没有逻辑关系,有的话叫有序树,没有叫无序树

森林 
 总

2.二叉树性质

①度可以理解为每个结点头上的树枝

 ①完全二叉树给了结点总数可推度为0,1,2的结点个数

树的存储结构

完全二叉树的顺序存储

 ①非完全二叉树顺序存储的一种存储方法

②这种方式会出现大批空间闲置的情况

①只有完全二叉树适合顺序存储 ①根据实际情况选择要不要添加父指针

3.二叉树遍历

 ①先序遍历第一次路过结点的时候就访问该结点

②优先往左走,其余二种方式相同

 ②第二次路过结点是访问该结点

①第三次路过时,访问该结点 

排序

代码

快速排序

#include<stdio.h>
#include<stdlib.h>
#define InitSize 10 //默认的最大长度



int Paration(int A[],int low,int high){
    int pivot=A[low];
    while (low<high){
        while (low<high&&A[high]>=pivot) high--;
        A[low]=A[high];
        while (low<high&&A[low]<pivot) low++;
        A[high]=A[low];
    }
    A[low]=pivot;
    return low;
}

void QuickSort(int A[],int low,int high){//数组就是地址传递,将数组首地址传到形参中
    if(low<high){//递归跳出条件
        int pivotpos=Paration(A,low,high);
        QuickSort(A,low,pivotpos-1);
        QuickSort(A,pivotpos+1,high);
    }
}
int main() {
    int A[]={3,2,1,4,5};
    QuickSort(A,0,4);
    for (int i = 0; i < 5; ++i) {
        printf("%d",A[i]);
    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值