【MOOC课程】浙大数据结构记录(上)

一些碎碎念:其实以前听过清华大学的 数据结构 OJ小记录(差不多快正好一年前了),而且也看到过一个博主曾经这两门课都上过对比过不同【清华看重课程深层的理论,浙大更轻松易懂一点】,可惜清华大学的上到上半段后就没有坚持下去搞完(主要是又看其他的课去了 热度变得好快哦 😂) 这次的笔记也主要是关于编程作业和自己一开始理解错了的地方。希望这次重拾能更深刻一点,这次尽量不像上次一样遇到不会就搜 至少我先挣扎个2小时… 不过做完一般我都会对比优劣和简易 如果有的话 wa 眼前一亮的那种 我会再次引用说明
之所以没有直接刷算法题,是因为我还是想从理论抓起的感觉会更扎实一点?虽然刷算法题也能接触到

【MOOC课程】浙大数据结构记录(下) 在这里:https://blog.csdn.net/qq_39537898/article/details/120015082
其实有想过把他们合并,但是太太太太长了 hhhh 所以就还是分一下上下 当做自我记录了。
giteee代码链接:https://gitee.com/kin_zhang/data-structure

第一周:基本概念 (二分)

编程作业

第一周的题目是:

  1. 最大子列和问题:是本次课最后讲到的4种算法的实验题,属于基本要求,一定要做
  2. Maximum Subsequence Sum:是2004年浙江大学计算机专业考研复试真题,要求略高,选做。其实也不难,是本次课最后讲到的算法的改造,挑战一下吧~
  3. 二分查找:配合课后讨论题给出这道函数填空题,学有余力、并且会C语言编程的你可以尝试一下。你只需要提交一个函数,而不用交如main函数之类的其他函数。不会C语言的话,就研究一下课后关于二分法的讨论题吧~

01-复杂度1 最大子列和问题

给定K个整数组成的序列,“连续子列”被定义为{ N,其中 1≤i≤j≤K。“最大子列和”则被定义为所有连续子列元素的和中最大者。例如给定序列{ -2, 11, -4, 13, -5, -2 },其连续子列{ 11, -4, 13 }有最大的和20。现要求你编写程序,计算给定整数序列的最大子列和。

本题旨在测试各种不同的算法在各种数据情况下的表现。各组测试数据特点如下:

数据1:与样例等价,测试基本正确性;
数据2:102个随机整数;
数据3:103个随机整数;
数据4:104个随机整数;
数据5:105个随机整数;

输入格式:
输入第1行给出正整数K (≤100000);第2行给出K个整数,其间以空格分隔。
输出格式:
在一行中输出最大子列和。如果序列中所有整数皆为负数,则输出0。
输入样例:
6
-2 11 -4 13 -5 -2
输出样例:
20

这个四种方法,二分法我是照着伪代码直接复制了,数组传地址进去就OK的😂

然后是四种方法的运行截图对比

01-复杂度2 Maximum Subsequence Sum

Given a sequence of K integers { N1, N2, …, NK }. A continuous subsequence is defined to be { Ni, Ni+1, …, Nj } where 1. The Maximum Subsequence is the continuous subsequence which has the largest sum of its elements. For example, given sequence { -2, 11, -4, 13, -5, -2 }, its maximum subsequence is { 11, -4, 13 } with the largest sum being 20.

Now you are supposed to find the largest sum, together with the first and the last numbers of the maximum subsequence.

Input Specification:
Each input file contains one test case. Each case occupies two lines. The first line contains a positive integer K (≤). The second line contains K numbers, separated by a space.

Output Specification:
For each test case, output in one line the largest sum, together with the first and the last numbers of the maximum subsequence. The numbers must be separated by one space, but there must be no extra space at the end of a line. In case that the maximum subsequence is not unique, output the one with the smallest indices i and j (as shown by the sample case). If all the K numbers are negative, then its maximum sum is defined to be 0, and you are supposed to output the first and the last numbers of the whole sequence.

Sample Input:
10
-10 1 2 3 4 -5 -23 3 7 -21

Sample Output:
10 1 4

自己C语言写的 感觉没有很简洁,主要是第一次没有审题… 题目里要求,如果全部为负数,输出首尾,和为0,我一开始苦恼了很久… 为啥我的负数输出是错的 看了别人的才发现哦…霍没审题

// // No.4 online searching
#include <stdbool.h>
#include <stdio.h>
int main()
{
  int NUM_in; //number of input integer
  scanf("%d", &NUM_in);
  int i, temp, Test_SUM = 0, Final_SUM = 0;
  int record_left = 0, record_right = 0, temp_left, temp_first;
  bool if_record = false, if_all_negative = true;
  for (i = 0; i < NUM_in; i++)
  {
    scanf("%d", &temp);
    if (i == 0)	temp_first = temp;
    Test_SUM += temp;
    if (!if_record)
    {
      temp_left = temp;
      if_record = true;
    }
    if (Test_SUM > Final_SUM)
    {
      Final_SUM = Test_SUM;
      record_right = temp;
      record_left = temp_left;
    }
    else if (Test_SUM < 0)
    {
      Test_SUM = 0;
      if_record = false;
    }
    if (temp >= 0)
      if_all_negative = false;
  }
  if (if_all_negative)
  {
    record_left = temp_first;
    record_right = temp;
  }

  printf("%d %d %d", Final_SUM, record_left, record_right);
  return 0;
}

看到过用C++ vector写的很简洁的方式

#include <iostream>
#include <vector>
using namespace std;
int main()
{
    int T;
    cin>>T;int sum=0,tmp,max=-1;
    vector<int> vec;
    vector<int> res;
    vector<int> all;
    while(T--){
        cin>>tmp;
        all.push_back(tmp);
        vec.push_back(tmp);
        sum+=tmp;
        if(sum>max) {
            max=sum;
            res=vec;
        }
        if(sum<0){
            sum=0;
            vec.clear();
        }
    }
    if(max!=-1) cout<<max<<" "<<res[0]<<" "<<res[res.size()-1];
    else cout<<0<<" "<<all[0]<<" "<<all[all.size()-1];
    system("pause");
    return 0;
}

01-复杂度3 二分查找

L是用户传入的一个线性表,其中ElementType元素可以通过>、==、 <进行比较,并且题目保证传入的数据是递增有序的。函数BinarySearch要查找X在Data中的位置,即数组下标(注意:元素从下标1开始存储)。找到则返回下标,否则返回一个特殊的失败标记NotFound。

emmm 我又掉进自己自己挖的坑了,我以为要用迭代来做… 然后发现这个List指向地址,因为迭代过程中有新List所以新List地址未变导致迭代失败(主要是传不了left,right啥的) 后面看提示说讨论区,一看 大家伪代码里咋都带while呢,还有就是!元素从1开始存储啊!!! 从0开始的我… 怎么试怎么不对
顺带给大家这个:【好调试 但是之前的那位仁兄写的是i=0开始存储】

List ReadInput()
{
    List L; //初始化顺序表
    L = (List)malloc(sizeof(struct LNode));
    L->Last = -1;

    int n;
    scanf("%d", &n);
    L->Last = n ;
    for (int i = 1; i < (n+1); i++)
    {
        scanf("%d", &L->Data[i]);
    }
    return L;
}

解答:

Position BinarySearch( List L, ElementType X )
{
    Position begin = 1;
    Position end = L->Last;
    while(begin <= end) {
        Position mid = (begin + end) /2;
        if(L->Data[mid] > X) 
            end = mid - 1;
        else if(L->Data[mid] < X) 
            begin = mid + 1;
        else return mid;
    }
    return NotFound;
}

也就是如果比中间的大,尾端直接到mid前一个数,这时候你可能疑惑如果是第二个数(总数为奇数)怎么办,程序上 第一次是end=2,begin=1,mid=1了,下一次发现比mid=1的大,所以begin=2,end=2,mid=2那么这时候检查第二个数,
也就是说如果是 1 2 4 5 6,你输出的是查找2,
那么这个程序是检查完4,直接到mid=1(此时begin=1,end=1),对比1<2然后mid再回到2这时候输出;如果你输入的是3,那么mid回2的时候不符合,end=1<begin循环结束
感觉这个难点不在于二分法了,而是在于审题和理清楚begin,end直接的变化

第二周:线性结构(List Stack Queue)

2.1 讨论区

如果将链式存储中FindKth的函数实现(如下)做个修改:把函数最后的if语句判断条件改为判断p是否为NULL,即:

if (p==NULL) return NULL;
else return p;

或者说直接简化为:return p;
对于这样的修改,程序还正确吗?为什么?

List FindKth( int K, List PtrL )
{     List  p = PtrL;
       int  i = 1;
       while (p !=NULL && i < K ){
              p = p->Next;
              i++;  
       }
       if(i == K) return p;    /*找到第K个,返回指针*/
       else  return NULL;    /* 否则返回空 */
}

贴这个的原因主要是一开始,我觉得 哎 挺对的呀。😂
后面发现同学回答的:因为你要考虑,如果输入的K不合理不合法 比如我输入个 K =-1 第-1个数,应该要返回空,但是改了之后的是返回的p也就是第一个链表

广义表

在这里插入图片描述

测试题

1. 在矩阵的多重链表表示中,第i行的head和第i列的head实际上是同一个结点

理由:在矩阵的多重链表表示中,每行、每列都需要有个头指针来指示相应链表的头一个元素结点。每个head结点有down、right和next三个指针,第 i 个head结点用它的right把第i行的结点串起来,同时用它的down指针把第i列串起来,而通过next把每行(每列)的head结点串起来。所以,第i行的head和第i列的head实际上是同一个结点,图中只是为了方便理解和图的简洁把同一个结点画在两处。

2. 下列函数试图求链式存储的线性表的表长,是否正确?

int  Length ( List  *PtrL )
{    List  *p = PtrL;      
     int  j = 0;
     while ( p ) { 
           p++; 
           j++;                 
     }   
     return  j;
}

因为这里p++ 不是数组,地址不是连续的指向。
字面上,p++指p=p+1, 跟 p=p->next显然不一样。
说一下应用场景上的区别:
数组的每个元素在内存里是挨在一起的,p指向一个元素,p++就是下一个元素,相当于一排人站一起,p++就指向旁边的下一个人。
链表是不同的结点通过指针,逻辑上连在一起。两个相连的结点在内存里不一定是连续。相当于你口袋里放着下一个人的联系方式,那个人可能在天涯海角。p=p->next, 就是p指向口袋里联系方式的那个人,而不是旁边那个人(p++).

参考:讨论区老师回答了 第一个 head同一结点
讨论区第二个 p++和p->next区别

编程作业

  1. 两个有序链表序列的合并 这是一道C语言函数填空题,训练最基本的链表操作。如果会用C编程的话,一定要做

  2. 一元多项式的乘法与加法运算 在“小白专场”里,我们会详细讨论C语言实现的方法。对于不会C语言而不能做第1题的同学,本题一定要做

  3. Reversing Linked List 根据某大公司笔试题改编的2014年春季PAT真题,不难,可以尝试;

  4. Pop Sequence 是2013年PAT春季考试真题,考察队堆栈的基本概念的掌握,应可以一试。

02-线性结构1 两个有序链表序列的合并

本题要求实现一个函数,将两个链表表示的递增整数序列合并为一个非递减的整数序列。

这一题非常有意思,主要是一开始我对链表的地址指向不明确,我以为是整体包括head一起的地址,后面才知道相等是地址与地址之间的不变,head算作一个数;【可能是C语言基础不扎实,对于链表这种地址的Next指针有点迷迷的 不知道我调试出来的解释方法能不能听懂】

#include <stdio.h>
#include <stdlib.h>

typedef int ElementType;
typedef struct Node *PtrToNode;
struct Node {
    ElementType Data;
    PtrToNode   Next;
};
typedef PtrToNode List;

List Read(); /* 细节在此不表 */
void Print( List L ); /* 细节在此不表;空链表将输出NULL */

List Merge( List L1, List L2 );

int main()
{
    List L1, L2, L;
    L1 = Read();
    L2 = Read();
    L = Merge(L1, L2);
    Print(L);
    Print(L1);
    Print(L2);
    return 0;
}
List Read()
{
    List head,tail,p;
    int i,n;
    scanf("%d",&n);
    for(i=0;i<=n;i++)
    {
        p=(List)malloc(sizeof(struct Node));
        p->Next=NULL;
        if(i==0)
        {
            head=tail=p;
        }
        else
        {
            scanf("%d",&p->Data);
            tail->Next=p;
            tail=p;
        }
    }
    return head;
}

void Print( List L )
{
    List t=L;
    if(t->Next==NULL)
    {
        printf("NULL\n");
    }
    else
    {
        while(t->Next)
        {
            printf("%d ",t->Next->Data);
            t=t->Next;
        }
        printf("\n");
    }
}

// /* 你的代码将被嵌在这里 */
List Merge( List L1, List L2 )
{
    List p1,p2,L,pL;
    pL=(List)malloc(sizeof(struct Node));
    L = pL;
    p1 = L1->Next;
    p2 = L2->Next;
    /* -------------- For Test --------------------- */
    // pL = L2;
    // pL->Next = L2;
    // pL = pL->Next;
    // pL->Next = L1;
    /* -------------- For Test --------------------- */
    while(p1&&p2)
    {
        if(p1->Data>p2->Data)
        {
            pL->Next = p2;
            p2 = p2->Next;
        }
        else
        {
            pL->Next = p1;
            p1 = p1->Next;
        }
        pL = pL->Next;
    }
    pL->Next = p1 ? p1 : p2;
    //这里的NULL为什么 在后面解释了
    L1->Next = NULL;
    L2->Next = NULL;
    return L;
}

自问:线性链表为什么相等之后 Next自动指向下一个?
自答:

    List p1,p2,L,pL;
    pL=(List)malloc(sizeof(struct Node));
    L = pL;

是这样的,第二行我们已经初始化了pL的head,然后L与pL绑定好了
此时 如果我们输入pL->Next=L2,我们可以看到L head后整个都是L2的。(L与pL的head一致,Next也一致,所以pL掌握着L的Next) 因为是地址指向
那么这样我们在替代的时候pL->Next=L2;后 pL=pL->Next;就是将L的Next再往后移了一个也就是整个L
而这里当pL=pL->Next;的时候为什么L没有变呢?因为pL掌握的是L的Next地址,pL=pL->Next的时候 Next地址并没有直接发生改变

pL->Next = L2; //Next地址直接发生改变,L->Next随之变换=L2
pL = pL->Next; //现在pL是与L->Next->Next绑定在一起的
pL->Next = L1; //>Next->Next地址直接发生改变,L->Next->Next随之变换=L1

可以采取断点调试看一下整个list的替代关系
第一幅图是运行L=pL 可以看到L与pL绑定了 地址一致
在这里插入图片描述
第二幅是:第78行pL->Next=L2运行后,L与pL的地址一致
在这里插入图片描述
但是第三幅图,这里,我们可以看到运行pL=L2后,L与pL彻底断连了,没有联系了 地址不一样了【这里是与第二幅图不一定的赋值程序了】
在这里插入图片描述
感兴趣的可以复制代码到vscode调试起来试一下不同的赋值,不同的顺序会对L产生的变化,整个程序逻辑上很简单对比大小,主要是链表的操作可能会引起一直不成功的现象。
比如最后L1,L2需要NULL也不是(如下)。所以对地址的操作才是不设限的

//不是这个 使L1,L2发生实质性变化
L1=NULL;
L2=NULL;

//而是这个使其发生变化
L1->Next=NULL;
L2->Next=NULL;

02-线性结构2 一元多项式的乘法与加法运算

设计函数分别求两个一元多项式的乘积与和。

输入格式:
输入分2行,每行分别先给出多项式非零项的个数,再以指数递降方式输入一个多项式非零项系数和指数(绝对值均为不超过1000的整数)。数字间以空格分隔。

输出格式:
输出分2行,分别以指数递降方式输出乘积多项式以及和多项式非零项的系数和指数。数字间以空格分隔,但结尾不能有多余空格。零多项式应输出0 0

这个不同于02-线性结构1的是,和一些坑:

  1. header 空的没有了,从一开始就是有coef和expon值的…
  2. MOOC小白专场里其实把程序逻辑及思路基本理清了,但是留了个小bug也就是coef==0的时候其实是不需要attach的所以if一下就好了

这里面我留了个问题,但是实践了一下,如果去掉是会导致死机的… 具体死在哪里也不太知道…Q1问题
代码:

#include <stdio.h>
#include <stdlib.h>

typedef struct PolyNode *Polynomial;
struct PolyNode {
    int coef;
    int expon;
    Polynomial link;
};
/* ------------- 定义的一些函数 ------------ */
Polynomial ReadPoly();
Polynomial Add(Polynomial P1, Polynomial P2);
Polynomial Mult(Polynomial P1, Polynomial P2);
void PrintPoly(Polynomial P);
void Attach(int c, int e, Polynomial *pRear);

int main()
{
    Polynomial P1, P2, PP, PS;
    P1 = ReadPoly();
    P2 = ReadPoly();
    PP = Mult(P1, P2);
    PrintPoly(PP);
    PS = Add(P1, P2);
    PrintPoly(PS);
    return 0;
}

Polynomial ReadPoly()
{
    Polynomial P, Rear, t;
    int N,c,e;
    scanf("%d", &N);
    P = (Polynomial)malloc(sizeof(struct PolyNode));
    P->link = NULL;
    Rear = P;

    while(N--)
    {
        scanf("%d %d", &c, &e);
        Attach(c, e, &Rear);
    }

    /* ---- 删除临时生成的头结点 ---- */
    t = P;//Q1问题:为什么这里需要出现t=P呢,如果没有的话 P = P->link直接指向不可以吗?
    P = P->link;
    free(t);
    /* ---- 删除临时生成的头结点 ---- */
    
    return P;
}

void Attach(int c, int e, Polynomial *pRear)
{
    if(c)
    {
        Polynomial P;
        P = (Polynomial)malloc(sizeof(struct PolyNode));
        P->coef = c;
        P->expon = e;
        P->link = NULL;
        (*pRear)->link = P;
        *pRear = P; //修改pRear的值
    }
    
}

Polynomial Add(Polynomial P1, Polynomial P2)
{
    Polynomial t1, t2, P, Rear,temp;
    t1 = P1;
    t2 = P2;
    P = (Polynomial)malloc(sizeof(struct PolyNode));
    P->link = NULL;
    Rear = P ;//= tempP
    while (t1&&t2)
    {
        if (t1->expon == t2->expon)
        {
            Attach(t1->coef + t2->coef, t1->expon, &Rear);
            t1 = t1->link;
            t2 = t2->link;
        }
        else if(t1->expon > t2->expon)
        {
            Attach(t1->coef, t1->expon, &Rear);
            t1 = t1->link;
        }
        else
        {
            Attach(t2->coef, t2->expon, &Rear);
            t2 = t2->link;
        }

    }
    Rear->link = t1 ? t1 : t2;
    temp = P;
    P = P->link;
    free(temp);
    return P;
}

Polynomial Mult(Polynomial P1, Polynomial P2)
{
    Polynomial t1, t2, P, Rear, t;
    int c, e;

    if(!P1 || !P2)
        return NULL;

    t1 = P1;
    t2 = P2;
    P = (Polynomial)malloc(sizeof(struct PolyNode));
    P->link = NULL;
    Rear = P ;//= tempP
    // //第一项的P1乘以每个t2 得出了一个Rear了
    // while (t2)
    // {
    //     Attach(t1->coef * t2->coef, t1->expon + t2->expon, &Rear);
    //     t2 = t2->link;
    // }
    while (t1)
    {
        t2 = P2;
        Rear = P;
        while (t2)
        {
            c = t1->coef * t2->coef;
            e = t1->expon + t2->expon;
            while (Rear->link && Rear->link->expon > e)
            {
                Rear = Rear->link;
            }
            if (Rear->link && Rear->link->expon == e)
            {
                if(Rear->link->coef + c)
                    Rear->link->coef += c;
                else
                {
                    t = Rear->link;
                    Rear->link = t->link;
                    free(t);
                }
            }
            else
            {
                t = (Polynomial)malloc(sizeof(struct PolyNode));
                t->coef = c;
                t->expon = e;
                t->link = Rear->link;
                Rear->link = t;
                Rear = Rear->link;
            }
            
            t2 = t2->link;
        }
        t1 = t1->link;
    }
    t2 = P;
    P = P->link;
    free(t2);

    return P;
}

void PrintPoly(Polynomial P)
{
    int flag = 0;
    if(!P)
    {
        printf("0 0\n");
        return;
    }
    while(P)
    {
        if(!flag)
            flag = 1;
        else
            printf(" ");
        printf("%d %d", P->coef, P->expon);
        P = P->link;
    }
    printf("\n");
}

02-线性结构3 Reversing Linked List

妈耶… 这个也太难顶了… 总共耗时了我大概三小时… 断续的,而且我知道我的复杂度肯定没办法完成最大的那个【都开始for-for了】… 我真的尽力了,一共有150多行… 情况也总是在分,虽然很长,但是还是记录一下自己的愚蠢:主要是我觉得我没有get数据结构的优美精髓… 所以我会回来在后面补上我的愚蠢记录,这次学习打开做之前绝对不看别人的代码!所以很多时候都是很蠢的 Hhhh 毕竟我真的很喜欢暴力解决问题
另外温馨提示:printf("%05d",num);可以按照往补0的方式补全五位,但是对于-1就变成-00001了哦,需要另开if,建议还是直接翻后面的参考的简洁高效的代码 ->这里是参考链接:C++版本

#include <stdio.h>
#include <stdlib.h>

typedef struct Linked_List *List;
struct Linked_List {
    int Addr;
    int Data;
    int Next_Addr;
    List Next;
};
int first_Addr,N, Reverse;//global variables
List ReadList();
void Attach(int Addr, int Data, int Next_Addr, List *pRear);
List SortList(List L);
void DelteNode(List *pRear);
List ReveredList(List L);
void PrintList(List L);
void FindKth_Modify_Addr(int RF, int K, List L);
List FindKth(int K, List L);
int main()
{
    List Original_list, Sorted_list, Reversed_list;
    Original_list = ReadList();
    if(N!=1)
    {
        Sorted_list = SortList(Original_list);
        Reversed_list = ReveredList(Sorted_list);
    }
    else
    {
        Reversed_list = Original_list;
    }
    PrintList(Reversed_list);
    return 0;
}

List ReadList()
{
    List L, Rear, t;
    int Addr, Data, Next_Addr;
    L = (List)malloc(sizeof(struct Linked_List));
    L->Next = NULL;
    Rear = L;

    scanf("%d %d %d", &first_Addr, &N, &Reverse);
    for (int i = N; i > 0;i--)
    {
        scanf("%d %d %d", &Addr, &Data, &Next_Addr);
        Attach(Addr, Data, Next_Addr, &Rear);
    }

    /* ---- 删除临时生成的头结点 ---- */
    t = L;//为什么这里需要出现t=P呢,如果没有的话 P = P->link直接指向不可以吗?
    L = L->Next;
    free(t);
    /* ---- 删除临时生成的头结点 ---- */
    
    return L;
}

void Attach(int Addr, int Data, int Next_Addr, List *pRear)
{
    List L;
    L = (List)malloc(sizeof(struct Linked_List));
    L->Addr = Addr;
    L->Data = Data;
    L->Next_Addr = Next_Addr;
    L->Next = NULL;
    (*pRear)->Next = L;
    (*pRear) = L;
}

List SortList(List L)
{
    List sorted_L,Rear,move_L,sortedL_Rear,fo_Rear,t;
    int for_num;
    sorted_L=(List)malloc(sizeof(struct Linked_List));
    sortedL_Rear = sorted_L;
    Rear = L;
    while(Rear!=NULL && Rear->Addr != first_Addr)
    {
        fo_Rear = Rear;
        Rear = Rear->Next;
    }
    if(Rear->Addr == first_Addr)
    {
        Attach(Rear->Addr, Rear->Data, Rear->Next_Addr, &sortedL_Rear);
        DelteNode(&fo_Rear);
        for_num = N - 1;
        Rear = L;
        N = 1;
    }
    for (int i = for_num; i > 0;i--)
    {
        for (int j = for_num; j > 0;j--)
        {
            if(Rear->Addr == sortedL_Rear->Next_Addr)
            {
                Attach(Rear->Addr, Rear->Data, Rear->Next_Addr, &sortedL_Rear);
                N++;
                break;
            }
            Rear = Rear->Next;
        }
        Rear = L;
    }

    /* ---- 删除临时生成的头结点 ---- */
    t = sorted_L;//为什么这里需要出现t=P呢,如果没有的话 P = P->link直接指向不可以吗?
    sorted_L = sorted_L->Next;
    free(t);
    /* ---- 删除临时生成的头结点 ---- */
    return sorted_L;
}
void DelteNode(List *pRear)
{
    List s;
    s = (*pRear)->Next;
    (*pRear)->Next = s->Next;
    free(s);
}

List ReveredList(List L)
{
    List Reversed_L;
    int RFromN = N / Reverse;
    int j = RFromN;
    int i = j * Reverse;
    if(Reverse == 1)
    {
        Reversed_L = L;
    }
    else
    {
        for (; j > 0;j--)
        {
            for (; i > (j - 1) * Reverse; i--)
            {
                FindKth_Modify_Addr(j, i, L);
            }
        }
        Reversed_L = SortList(L);
    }
    return Reversed_L;
}

void FindKth_Modify_Addr(int RFj, int K, List L)
{
    int RFromN = N / Reverse;
    List p = L,fo_p;
    int i = 1;
    while(p!=NULL && i<K)
    {
        fo_p = p;
        p = p->Next;
        i++;
    }
    if(i==K)
    {
        if(RFj != RFromN && i%Reverse == 1)
        {
            p->Next_Addr = FindKth((RFj+1)*Reverse, L)->Addr;
        }
        else if(Reverse == N && i == 1)
        {
            p->Next_Addr = -1;
        }
        else if(RFj == RFromN && i == 1)
        {
            p->Next_Addr = FindKth(Reverse, L)->Next->Addr;
        }
        else if(i == Reverse)
        {
            first_Addr = p->Addr;
            p->Next_Addr = fo_p->Addr;
        }
        else if(Reverse * RFromN == N && i == ((RFromN-1)*Reverse +1))
        {
            p->Next_Addr = -1;
        }
        else
        {
            p->Next_Addr = fo_p->Addr;
        }
    }
    else
    {
        printf("ERROR");
    }
}

void PrintList(List L)
{
    List p = L;
    while(p!=NULL)
    {
        if(p->Next_Addr == -1)
        {
            printf("%05d %d %d\n", p->Addr, p->Data, p->Next_Addr);
        }
        else
            printf("%05d %d %05d\n", p->Addr, p->Data, p->Next_Addr);
        p = p->Next;
    }
}

List FindKth(int K, List L)
{
    List p = L;
    int i = 1;
    while(p!=NULL && i<K)
    {
        p = p->Next;
        i++;
    }
    if(i==K)
        return p;
    else
        return NULL;
}

C++超简洁版本:markconca的博客
思路

#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;
const int maxn=1e6+15;
// 直接构建了一个node的结构体数组 直接拿地址当做索引
struct Node
{
    int next;
    int data;
 
}node[maxn+1];

int List[maxn+1];//List数组存的是地址
int main()
{
 
    int first,n,k;
    int ddata,nnext,add;
    cin>>first>>n>>k;//读入第一个地址add,个数N,翻转长度K
    int p=first;
    for(int i=0; i<n; i++)
    {
       cin>>add>>ddata>>nnext;
       node[add].data=ddata;
       node[add].next=nnext;
    }
 
    int j=0;
 	
 	//这边是则是按顺序就是从第一个地址读到下一个地址的顺序 排好
    while(p!=-1)
    {
        List[j++]=p;//从第一个地址默认p=first
        p=node[p].next;//这里就是按地址索引node数组得到下一个next
    }
    int i=0;//从第一个数字+k = 开始反转
    while((i+k)<=j)
    {
        reverse(&List[i],&List[i+k]);
        i=i+k;
    }
    for(int i=0; i<j-1; i++)
        printf("%05d %d %05d\n",List[i],node[List[i]].data,List[i+1]);//然后继续拿地址直接索引
    printf("%05d %d -1\n",List[j-1],node[List[j-1]].data);
    return 0;
}

在这里面用到了C++本身有的一个reverse函数 C++ reverse官方介绍
和sort用法基本相近,最常用两个参数时,第一个为起点地址,第二个为操作的数目。
eg1:reverse(a,a+10); 类似于sort。
eg2:reverse (&a[3],&a[3+5]);为从下标三开始的五个数,进行反转。【是所有的3-8之间全部翻转】
0 1 2 3 4 5 6 7 8 9 - > 9 8 7 6 5 4 3 2 1 0
添加了一些我的注解,整体来说就是 妙啊!【其实主要是reverse都由函数完成了】

02-线性结构4 Pop Sequence

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct SNode *PtrToSNode;
typedef int ElementType;
struct SNode {
    ElementType Data;
    PtrToSNode Next;
};
typedef PtrToSNode Stack;

Stack CreateStack();

bool IsEmpty(Stack S);

bool Push(Stack S, ElementType X);

ElementType Pop(Stack S);
int main()
{
 
    int M,N,K,Data;
    bool Mark_check=true;
    bool Max_num=false,Max_size=false;
    //M:maximum capacity of the stack
    //N:length of push sequence
    //K:the number of pop sequences to be checked
    scanf("%d %d %d", &M, &N, &K);
    for (int i = 0; i < K; i++)
    {
        /*  ---------------- INIT --------------------- */
        Stack S_orig = CreateStack();
        Push(S_orig, 1);
        int S_data = 2,count_size=1;
        Max_num = false;
        Max_size = false;
        Mark_check = true;
        /*  ---------------- INIT --------------------- */

        for (int j = 0; j < N; j++)
        {
            scanf("%d", &Data);
            if(Mark_check)
            {
                if(IsEmpty(S_orig)&&!Max_num&&!Max_size)
                {
                    Push(S_orig, S_data);
                    S_data++;
                    count_size++;
                }
                while(Data != S_orig->Next->Data&&!Max_num&&!Max_size)
                {
                    Push(S_orig, S_data);
                    Mark_check = false;
                    S_data++;
                    count_size++;
                    Max_size = count_size >= M ? true : false;
                }
                if(Data == S_orig->Next->Data)
                {
                    Pop(S_orig);
                    count_size--;
                    Mark_check=true;
                }
                else
                {
                    Mark_check=false;
                }
                if(S_data>N)
                    Max_num = true;
                Max_size = count_size > M ? true : false;
            }
        }
        if(Mark_check)
            printf("YES\n");
        else
            printf("NO\n");
    }
    // printf("DONE\n");
    return 0;
}

Stack CreateStack() 
{ /* 构建一个堆栈的头结点,返回该结点指针 */
    Stack S;

    S = (Stack)malloc(sizeof(struct SNode));
    S->Next = NULL;
    return S;
}

bool IsEmpty ( Stack S )
{ /* 判断堆栈S是否为空,若是返回true;否则返回false */
    return ( S->Next == NULL );
}

bool Push( Stack S, ElementType X )
{ /* 将元素X压入堆栈S */
    PtrToSNode TmpCell;

    TmpCell = (PtrToSNode)malloc(sizeof(struct SNode));
    TmpCell->Data = X;
    TmpCell->Next = S->Next;
    S->Next = TmpCell;
    return true;
}
ElementType Pop( Stack S )  
{ /* 删除并返回堆栈S的栈顶元素 */
    PtrToSNode FirstCell;
    ElementType TopElem;

    if( IsEmpty(S) ) {
        printf("堆栈空"); 
        return -1;
    }
    else {
        FirstCell = S->Next; 
        TopElem = FirstCell->Data;
        S->Next = FirstCell->Next;
        free(FirstCell);
        return TopElem;
    }
}

第三周:树 (二叉树 构建)

客观世界中许多事物存在层次关系,原因:高效

查找 searching:根据某个给定关键字K 从集合R中找出关键字与K相同的纪录

  • 静态:集合固定
  • 动态:集合动态变化,可能会发生插入和删除

顺序查找:从下标最大处一直往前找

二分查找

二叉树遍历的核心问题:二维结构的线性化

  • 从结点访问其左、右 儿子结点
  • 访问左儿子后,右儿子怎么办?
    • 需要一个存储结构来保存暂时不访问的节点
    • 存储结构:堆栈、队列

3.1 问题

  1. 有一个m棵树的集合(也叫森林)共有k条边,问这m颗树共有多少个结点?
  1. 一棵度为 m的树有n个节点。若每个节点直接用m个链指向相应的儿子,则表示这个树所需要的总空间是n*(m+1) (假定每个链以及表示节点的数据域都是一个单位空间).。当采用儿子/兄弟(First Child/Next Sibling)表示法时,所需的总空间是:
    首先是要注意到假定每个链以及表示节点的数据域都是一个单位空间不然容易陷入死胡同 百度参考

在这里插入图片描述

  1. 有一颗二叉树,其两个儿子的结点个数为15个,一个儿子的结点个数为32个,问该二叉树的叶结点个数是多少?
    n 0 = n 2 + 1 n_0=n_2+1 n0=n2+1 这里的两个儿子的结点个数为15是指 节点处有2个儿子的个数,也就是 n 2 n_2 n2的值,而叶结点也就是下面没儿子了 n 0 = 15 + 1 = 16 n_0=15+1=16 n0=15+1=16

3.1 讨论区

树的集合称为森林

是否也可以使用“儿子-兄弟”表示法存储森林?如何实现?

在这里我们可以看到一颗树的表示方式,在根节点的Next Sibling是NULL所以可以把一颗颗树通过这个点连起来成为森林:

第一个方法:(增加虚拟节点)

需要增加一个虚拟的根节点。森林中的所有树的根节点都是该节点的孩子节点,然后再使用长子兄弟表示法。
在这里插入图片描述

第二个方法:(利用根节点的兄弟指针)
在这里插入图片描述
可以,根节点的兄弟指针域用作连接森林中不同树根节点的指针,森林入口就是最左侧树的根节点

讨论3.3 m叉树中各类结点数之间的关系

在二叉树中,我们知道叶结点总数与有两个儿子的结点总数之间的关系是: n 0 = n 2 + 1 n_0=n_2+1 n0=n2+1
那么类似关系是否可以推广到m叉树中?也就是,如果在m叉树中,叶结点总数是,有一个儿子的结点总数是 n 1 n_1 n1,有2个儿子的结点总数是 n 2 n_2 n2,有3个儿子的结点总数是 n 3 n_3 n3,…。那么, n i n_i ni之间存在什么关系?

首先和二叉树一样的是:全部结点总数想加-1 = 所有的边
n 0 + n 1 + n 2 + . . . + n m − 1 = 0 ∗ n 0 + 1 ∗ n 1 + 2 ∗ n 2 + . . . . + m ∗ n m n_0+n_1+n_2+...+n_m-1=0*n_0+1*n_1+2*n_2+....+m*n_m n0+n1+n2+...+nm1=0n0+1n1+2n2+....+mnm

化简后得: n 0 = 1 ∗ n 2 + 2 ∗ n 3 + . . . + ( m − 1 ) ∗ n m + 1 n_0=1*n_2+2*n_3+...+(m-1)*n_m+1 n0=1n2+2n3+...+(m1)nm+1

3.2

  1. 如果一个完全二叉树最底下一层为第六层(根为第一层)且该层共有8个叶结点,那么该完全二叉树共有多少个结点?
    在这里插入图片描述

  2. 设深度为d(只有一个根结点时,d为1)的二叉树只有度为0和2的结点,则此类二叉树的结点数至少为2d-1
    在这里插入图片描述

编程作业

  1. 树的同构 小白专场会做详细讲解,基本要求一定要做
  2. List Leaves 训练建树和遍历基本功一定要做
  3. Tree Traversals Again 是2014年秋季PAT甲级考试真题,稍微要动下脑筋,想通了其实程序很基础,建议尝试。

03-树1 树的同构

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAX_SIZE 10
#define Null -1
typedef int Tree;
typedef char Element_type;
struct BinaryTree
{
    Element_type Element;
    Tree Left;
    Tree Right;
}T1[MAX_SIZE], T2[MAX_SIZE];

Tree BuildTree(struct BinaryTree T[]);
bool Isomorphic(Tree R1, Tree R2);

int main()
{
    Tree R1, R2;
    R1 = BuildTree(T1);
    R2 = BuildTree(T2);
    if(Isomorphic(R1,R2))
        printf("Yes\n");
    else
        printf("No\n");
    return 0;
}

Tree BuildTree(struct BinaryTree T[])
{
    int i, N, check[MAX_SIZE], Root=Null;
    Element_type cl, cr;
    scanf("%d\n", &N);
    for (i = 0; i < N; i++)
        check[i] = 0;
    if(N)
    {    
        for (i = 0; i < N;i++)
        {
            scanf("%c %c %c\n", &T[i].Element, &cl, &cr);
            if(cl!='-')
            {
                T[i].Left = cl - '0';
                check[T[i].Left] = 1;
            }
            else
                T[i].Left = Null;
            if(cr!='-')
            {
                T[i].Right = cr - '0';
                check[T[i].Right] = 1;
            }
            else
                T[i].Right = Null;
            
        }
        for (i = 0; i < N; i++)
            if(!check[i])
                break;
        Root = i;
    }

    return Root;
}

bool Isomorphic(Tree R1, Tree R2)
{
    if(R1==Null && R2==Null)
        return true;
    else if((R1==Null && R2!=Null) || (R1!=Null && R2==Null))
        return false;
    else if(T1[R1].Element != T2[R2].Element)
        return false;
    else if (T1[R1].Left == Null && T2[R2].Left == Null)
        return Isomorphic(T1[R1].Right, T2[R2].Right);

    if ((T1[R1].Left != Null && T2[R2].Left != Null) && (T1[T1[R1].Left].Element == T2[T2[R2].Left].Element))
        return Isomorphic(T1[R1].Left, T2[R2].Left) && Isomorphic(T1[R1].Right, T2[R2].Right);
    else
        return Isomorphic(T1[R1].Left, T2[R2].Right) && Isomorphic(T1[R1].Right, T2[R2].Left);
}

03-树2 List Leaves

这里用的是书上说的层序遍历法,用的是队列来进行

#include <queue>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
using namespace std;
/* ---------- 此处定义见小白专场 -------------- */
#define MaxTree 10
#define ElementType int
#define Tree int
#define Null -1
/* ---------- 此处定义见小白专场 -------------- */
struct TreeNode
{
    ElementType Element;
    Tree Left;
    Tree Right;
} T1[MaxTree];
//在这里直接定义除了T1

//构建树结构
Tree BuildTree(struct TreeNode T[]);

void PrintOrder(Tree R);

int main()
{
    Tree R1;
    R1 = BuildTree(T1);
    PrintOrder(R1);
    return 0;
}

Tree BuildTree(struct TreeNode T[])
{
    int i,check[MaxTree],N;//Tree的大小
    char cl, cr;//临时存储输入字符
    Tree Root = Null;
    scanf("%d\n", &N);
    if (N)
    {
        //用来检查根在哪里
        for (i = 0; i < N; i++)
            check[i] = 0;
        for (i = 0; i < N; i++)
        {
            T[i].Element = i;
            scanf("%c %c\n", &cl, &cr);
            if(cl!='-')
            {
                T[i].Left = cl - '0';
                check[T[i].Left] = 1;//也就是说Left是有人指向的 对应的那个人就不是根节点
            }
            else
                T[i].Left = Null;

            if(cr!='-')
            {
                T[i].Right = cr - '0';
                check[T[i].Right] = 1;//也就是说Left是有人指向的 对应的那个人就不是根节点
            }
            else
                T[i].Right = Null;
        }
        for (i = 0; i < N ; i++)
            if (!check[i])
                break;
        Root = i;
    }
    return Root;
}
void PrintOrder(Tree R)
{
    Tree T;
    queue<int> qTree;
    qTree.push(R);
    while(!qTree.empty())
    {
        T = qTree.front();
        qTree.pop();
        if(T1[T].Left != Null)
            qTree.push(T1[T].Left);
        if(T1[T].Right != Null)
            qTree.push(T1[T].Right);
        if(T1[T].Left == Null && T1[T].Right == Null && !qTree.empty())
            printf("%d ", T);
        if(T1[T].Left == Null && T1[T].Right == Null && qTree.empty())
            printf("%d", T);
    }
}

03-树3 Tree Traversals Again

首先是中序遍历的建树,然后后序遍历输出

#include <queue>
#include <stack>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
using namespace std;

#define MaxTree 30
#define Tree int
#define Null -1

struct TreeNode
{
    Tree Left;
    Tree Right;
} T1[MaxTree];

//输入树处理
Tree InputTree(struct TreeNode T[]);

//后序遍历
void PostOrder(Tree Root);
Tree r;
int main()
{
    
    r = InputTree(T1);
    PostOrder(r);
    return 0;
}
Tree InputTree(struct TreeNode T[])
{
    int N, node, i = 0, Root = 0;
    Tree last_one, next_one, pop_last_one;
    bool IsLastActionPush;
    char w1, w2, w3, tw;
    stack<int> temp_Q;
    scanf("%d\n", &N);
    while(!temp_Q.empty() || i != N)
    {
        scanf("%c%c", &w1, &w2);
        if (w2=='u')
        {
            scanf("%c%c %d\n", &w3, &tw, &node);

            if(i==0)
                Root = node;
            else if(!IsLastActionPush)//如果上一步是pop
                T[pop_last_one].Right = node;
            else//如果是push
                T[last_one].Left = node;
            i++;
            last_one = node;
            temp_Q.push(node);
            IsLastActionPush = true;
        }
        else
        {
            scanf("%c\n", &w3);
            next_one = temp_Q.top();
            temp_Q.pop();
            if(IsLastActionPush && T[next_one].Left!= Null && T[next_one].Right != Null)
            {
                T[next_one].Left = Null;
                T[next_one].Right = Null;
                pop_last_one = next_one;
            }
            else if(!IsLastActionPush)//如果上一步是pop
                pop_last_one = next_one;

            IsLastActionPush = false;
        }
    }
    return Root;
}

void PostOrder(Tree Root)
{
    if(Root != Null && Root != 0)
    {
        PostOrder(T1[Root].Left);
        PostOrder(T1[Root].Right);
        if(Root != r)
            printf("%d ", Root);
        else
            printf("%d", Root);
    }
}

第四周:树 (平衡二叉树 AVL)

4.2问题

若一AVL树的结点数是21,则该树的高度至多是多少?注:只有一个根节点的树高度为0

def compute_most(n1):
    if n1==0:#高度为0
        return 1 #仅根节点
    elif n1==1:#高度为1
        return 3 #最多的节点为全满 即 3
    else:
        return 2*compute_most(n1-1)+2*compute_most(n1-2)-1
    
def compute_less(n1):
    if n1==0:#高度为0
        return 1#仅根节点
    elif n1==1:#高度为1
        return 2#最少的节点为根节点+左/右一边 即 2
    else:
        return compute_less(n1-1)+compute_less(n1-2)+1

for i in range(0,8):
    print("height:",i,"node number",compute_less(i))

然后可以知道高度5至少需要20个,然后到高度6至少需要33个,所以21个节点数,高度至多为5,

编程作业

  1. 是否同一棵二叉搜索树 小白专场将详细介绍C语言实现方法,属于基本训练,一定要做

  2. Root of AVL Tree 2013年浙江大学计算机学院免试研究生上机考试真题,是关于AVL树的基本训练,一定要做

  3. Complete Binary Search Tree 2013年秋季PAT甲级真题,略有难度,量力而行。第7周将给出讲解。

  4. 二叉搜索树的操作集 用C语言的同学,可以在这里把老师讲过的各种操作都试用一下。

04-树4 是否同一棵二叉搜索树

!!找了差不多半小时的问题… 被这篇博文点醒

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct TreeNode *Tree;
struct TreeNode {
    int v;
    Tree Left, Right;
    int flag;//是否被访问过
};
Tree MakeTree(int N);
Tree NewNode(int input);
Tree Insert(Tree T, int input);
int check(Tree T, int input);
int Judge(Tree T, int N);
void ResetT(Tree T);
void FreeTree(Tree T);

int main()
{
    int N, L,i;
    Tree T;
    scanf("%d", &N);
    while(N)
    {
        scanf("%d", &L);
        T = MakeTree(N);
        for (i=0; i < L;i++)
        {
            if(Judge(T,N))
                printf("Yes\n");
            else
                printf("No\n");
            ResetT(T);//清空flag标志
        }
        FreeTree(T);//清空树重建
        scanf("%d", &N);
    }
    // printf("No\n");
    return 0;
}

Tree MakeTree(int N)
{
    int input,i;
    Tree T;
    scanf("%d", &input);
    T = NewNode(input);
    for (i = 1; i < N; i++)
    {
        scanf("%d", &input);
        T = Insert(T,input);
    }
    return T;

}

Tree NewNode(int input)
{
    Tree T = (Tree)malloc(sizeof(struct TreeNode));
    T->v = input;
    T->Left = T->Right = NULL;
    T->flag = 0;
    return T;
}

Tree Insert(Tree T, int input)
{
    if(!T)//空树
        T = NewNode(input);
    else
    {
        if(input>T->v)
            T->Right = Insert(T->Right, input);
        else
            T->Left = Insert(T->Left, input);
    }
    return T;
}
int check(Tree T, int input)
{
    if(T->flag)
    {
        if(input>T->v)
            return check(T->Right, input);
        else if(input<T->v)
            return check(T->Left, input);
        else
            return 0;
    }
    else
    {
        if(input == T->v)
        {
            T->flag = 1;
            return 1;
        }
        else
            return 0;
    }
}
int Judge(Tree T, int N)
{
    int input,i;
    int Is_Judge_done = 0;
    scanf("%d", &input);
    if(input!=T->v)
        Is_Judge_done = 1;
    else
        T->flag = 1;

    for (i= 1; i < N;i++)
    {
        scanf("%d", &input);
        if((!Is_Judge_done) && (!check(T,input)))
            Is_Judge_done = 1;
    }
    if(Is_Judge_done)
        return 0;
    else
        return 1;
}

void ResetT(Tree T)
{
    if(T->Left)
        ResetT(T->Left);
    if(T->Right)
        ResetT(T->Right);
    T->flag = 0;//注意看!这里没有else
    //不然清除的时候就清除了叶子节点的
}
void FreeTree(Tree T)
{
    if(T->Left)
        FreeTree(T->Left);
    if(T->Right)
        FreeTree(T->Right);
    free(T);//注意看!这里没有else
}

04-树5 Root of AVL Tree

#include <stdio.h>
#include <stdlib.h>

typedef struct TreeNode *Tree;
struct TreeNode {
    int v;
    Tree Left, Right;
    int height;
};
Tree NewNode(int input);
int MakeTree(int N);
Tree Insert(Tree T, int input);
Tree SingleLeftRotation(Tree A);
Tree SingleRightRotation(Tree A);
Tree DoubleLeftRightRotation(Tree A);
Tree DoubleRightLeftRotation(Tree A);
int Max(int a, int b);
int GetHeight(Tree T);
int MakeTree(int N)
{
    int input,i;
    Tree T = NULL;
    for (i = 0; i < N; i++)
    {
        scanf("%d", &input);
        T = Insert(T,input);
    }
    return T->v;

}

Tree NewNode(int input)
{
    Tree T = (Tree)malloc(sizeof(struct TreeNode));
    T->v = input;
    T->Left = T->Right = NULL;
    T->height = 1;
    return T;
}

Tree Insert(Tree T, int input)
{
    if(!T)//空树
        T = NewNode(input);
    else if(input==T->v)
    {
        //do nothing
    }
    else if(input>T->v)
    {
        T->Right = Insert(T->Right, input);
        if(GetHeight(T->Left) - GetHeight(T->Right) == -2)
        {
            if (input>T->Right->v)//因为前面insert了右子树 所以右子树一定有值
                T = SingleRightRotation(T);
            else
                T = DoubleRightLeftRotation(T);
        }
    }
    else if(input<T->v)
    {
        T->Left = Insert(T->Left, input);
        if(GetHeight(T->Left) - GetHeight(T->Right)==2)
        {
            if (input<T->Left->v)
                T = SingleLeftRotation(T);
            else
                T = DoubleLeftRightRotation(T);
        }
    }
    T->height = Max(GetHeight(T->Left), GetHeight(T->Right)) + 1;

    return T;
}
int Max(int a,int b)
{
    return a > b ? a : b;
}
int GetHeight(Tree T)
{
    if(T != NULL)
        return T->height;
    else
        return 0;
}
Tree SingleLeftRotation(Tree A)
{
    Tree B = A->Left;
    A->Left = B->Right;
    B->Right = A;
    A->height = Max(GetHeight(A->Left), GetHeight(A->Right)) + 1;
    B->height = Max(GetHeight(B->Left), A) + 1;
    return B;
}
Tree SingleRightRotation(Tree A)
{
    Tree B = A->Right;
    A->Right = B->Left;
    B->Left = A;
    A->height = Max(GetHeight(A->Left), GetHeight(A->Right)) + 1;
    B->height = Max(GetHeight(B->Right), A) + 1;
    return B;
}

Tree DoubleLeftRightRotation(Tree A)
{
    A->Left = SingleRightRotation(A->Left);
    return SingleLeftRotation(A);
}
Tree DoubleRightLeftRotation(Tree A)
{
    A->Right = SingleLeftRotation(A->Right);
    return SingleRightRotation(A);
}
int main()
{
    int N, L;
    scanf("%d", &N);
    L = MakeTree(N);
    printf("%d\n", L);
    return 0;
}

04-树6 Complete Binary Search Tree

首先 这题我是间断了,间断的意思是:我中途断了3个月的学习,后面又拾起来的,然后这题我绞尽脑汁也没有能拿到满分(主要是快写了一天了 左右旋全完忘了 全靠trick来的)

其中层序输出因为用的不是C++(要是C++的话 vector就非常好用 emmm)二叉树的层序输出借鉴了:https://blog.csdn.net/verhappy/article/details/8859680

最后这是自己的代码:之所以说是trick是首先我的num就没对… 但是样例对了,我就真的… 有点改不下去了;其次我基本大感觉构思完了写完就开始调试,遇到什么问题加入if hhhh 我感觉我这样不太行

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct TreeNode *Tree;
struct TreeNode {
    int v;
    Tree Left, Right;
    int height;
    int num;
};

Tree NewNode(int input);
int MakeTree(int N);
Tree Insert(Tree T, int input);
Tree SingleLeftRotation(Tree A);
Tree SingleRightRotation(Tree A);
Tree DoubleLeftRightRotation(Tree A);
Tree DoubleRightLeftRotation(Tree A);
int Max(int a, int b);
int GetHeight(Tree T);
int GetNum(Tree T);

void LevelOrder_PrintBiTree(Tree T);
int MakeTree(int N)
{
    int input,i;
    Tree T = NULL;
    for (i = 0; i < N; i++)
    {
        scanf("%d", &input);
        T = Insert(T,input);
    }
    LevelOrder_PrintBiTree(T);
    return T->v;

}

Tree NewNode(int input)
{
    Tree T = (Tree)malloc(sizeof(struct TreeNode));
    T->v = input;
    T->Left = T->Right = NULL;
    T->height = 1;
    T->num = 1;
    return T;
}

Tree Insert(Tree T, int input)
{
    if(!T)//空树
        T = NewNode(input);
    else if(input==T->v)
    {
        //do nothing
    }
    else if(input>T->v)
    {
        T->Right = Insert(T->Right, input);
        if(GetHeight(T->Left) - GetHeight(T->Right) == -2)
        {
            if (input>T->Right->v)//因为前面insert了右子树 所以右子树一定有值
                T = SingleRightRotation(T);
            else
                T = DoubleRightLeftRotation(T);
        }
    }
    else if(input<T->v)
    {
        T->Left = Insert(T->Left, input);
        if(GetHeight(T->Left) - GetHeight(T->Right)==2)
        {
            if (input<T->Left->v)
                T = SingleLeftRotation(T);
            else
                T = DoubleLeftRightRotation(T);
        }
    }

    T->height = Max(GetHeight(T->Left), GetHeight(T->Right)) + 1;
    T->num = GetNum(T);

    if (T->Left!=NULL && T->Right!=NULL)
    {
        if(T->Left->num < T->Right->num && T->Left!=NULL)
        {
            T->Left = Insert(T->Left, T->v);
            T->v = T->Right->Left->v;
            if(T->Right->Left->Left != NULL)
                T->Left = Insert(T->Left, T->Right->Left->Left->v);
            T->Right->Left = NULL;
        }
        else if(T->Left->num < T->Right->num && T->Left==NULL)
        {
            T->Left->v = T->v;
            T->v = T->Right->v;
            T->Right = NULL;
        }
    }
    else if(T->Right!=NULL && T->Left==NULL)
    {
        int Temp = T->v;
         T->v = T->Right->v;
        T = Insert(T, Temp);
        T->Right = NULL;
    }
    T->height = Max(GetHeight(T->Left), GetHeight(T->Right)) + 1;
    T->num = GetNum(T);
    return T;
}
int GetNum(Tree T)
{
    if (T->Left!=NULL && T->Right!=NULL)
        return GetHeight(T->Left) + GetHeight(T->Right)+1;
    else
        return T->height;
}
int Max(int a,int b)
{
    return a > b ? a : b;
}
int GetHeight(Tree T)
{
    if(T != NULL)
        return T->height;
    else
        return 0;
}
Tree SingleLeftRotation(Tree A)
{
    Tree B = A->Left;
    A->Left = B->Right;
    B->Right = A;
    A->height = Max(GetHeight(A->Left), GetHeight(A->Right)) + 1;
    A->num = GetNum(A);
    B->height = Max(GetHeight(B->Left), A) + 1;
    B->num = GetNum(B);
    return B;
}
Tree SingleRightRotation(Tree A)
{
    Tree B = A->Right;
    A->Right = B->Left;
    B->Left = A;
    A->height = Max(GetHeight(A->Left), GetHeight(A->Right)) + 1;
    A->num = GetNum(A);
    B->height = Max(GetHeight(B->Right), A) + 1;
    B->num = GetNum(B);
    return B;
}

Tree DoubleLeftRightRotation(Tree A)
{
    A->Left = SingleRightRotation(A->Left);
    return SingleLeftRotation(A);
}
Tree DoubleRightLeftRotation(Tree A)
{
    A->Right = SingleLeftRotation(A->Right);
    return SingleRightRotation(A);
}

void LevelOrder_PrintBiTree( Tree T )
{
    Tree Bt[1001];
    int index = 1;
    int num = 0;
    if(T!=NULL)
        Bt[0] = T;
    while(true)
    {
        if(Bt[num]->Left!=NULL)
            Bt[index++] = Bt[num]->Left;
        if(Bt[num]->Right!=NULL)
            Bt[index++] = Bt[num]->Right;
        num++;
        if(num==index)
        {
            Bt[num] = NULL;
            break;
        }
    }
    num = 0;
    for (int i = 0; i < index-1; i++)
    {
        printf("%d ", Bt[i]->v);
    }
    printf("%d", Bt[index-1]->v);

}
int main()
{
    int N, L;
    scanf("%d", &N);
    L = MakeTree(N);
    // printf("%d\n", L);
    return 0;
}

这是单独看到图中章节,有讲这道题,发现!卧槽 这是我想复杂了啊,用链表去表示一个完全二叉搜索树得不偿失啊,建议想要完整理解思路 可以跳转第七讲 图(中)的视频——树之习题选讲

emmm 我… 我写完了 然后发现 我之前写的是真的麻烦啊!主要是得知道完全二叉树层数的一系列关系是很重要的

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#define MIN(x,y) ((x)>(y)?(y):(x))
#define MAX_SIZE 1000

int Tree[MAX_SIZE];
int CTree[MAX_SIZE];
void BuildComplete(int ALeft, int ARight, int TRoot);
int GetLeftLength(int n);
int compare(const void *a, const void *b)
{
    return *(int *)a - *(int *)b;
}
int main()
{
    int N;
    
    scanf("%d", &N);
    for (int i = 0; i < N; i++)
    {
        scanf("%d", &Tree[i]);
    }
    qsort(Tree, N, sizeof(int), compare);
    BuildComplete(0, N - 1, 0);
    printf("%d", CTree[0]);
    for (int i = 1; i < N; i++)
    {
        printf(" %d", CTree[i]);
    }
    return 0;
}
void BuildComplete(int ALeft, int ARight, int TRoot)
{
    int n = ARight - ALeft + 1;//现有总数
    if (n == 0)
        return;
    int L = GetLeftLength(n); //左子树个数
    CTree[TRoot] = Tree[ALeft + L];
    TRoot = TRoot * 2 + 1;
    BuildComplete(ALeft, ALeft + L - 1, TRoot);
    BuildComplete(ALeft + L + 1, ARight, TRoot + 1);
}
int GetLeftLength(int n)
{
    int H = floor(log2(n + 1));
    int X = (n + 1) - pow(2, H);
    return MIN(X, pow(2, H - 1)) + pow(2, H - 1) - 1;
}

04-树6 二叉搜索树的操作集

#include <iostream>
#include <cstdio>
#include <stack>
#include <stdlib.h>

typedef int ElementType;
typedef struct TNode *Position;
typedef Position BinTree;
struct TNode{
    ElementType Data;
    BinTree Left;
    BinTree Right;
};

void PreorderTraversal( BinTree BT ); /* 先序遍历,由裁判实现,细节不表 */
void InorderTraversal( BinTree BT );  /* 中序遍历,由裁判实现,细节不表 */

BinTree Insert( BinTree BST, ElementType X );
BinTree Delete( BinTree BST, ElementType X );
Position Find( BinTree BST, ElementType X );
Position FindMin( BinTree BST );
Position FindMax( BinTree BST );

int main()
{
    BinTree BST, MinP, MaxP, Tmp;
    ElementType X;
    int N, i;

    BST = NULL;
    scanf("%d", &N);
    for ( i=0; i<N; i++ ) {
        scanf("%d", &X);
        BST = Insert(BST, X);
    }
    printf("Preorder:"); PreorderTraversal(BST); printf("\n");
    MinP = FindMin(BST);
    MaxP = FindMax(BST);
    scanf("%d", &N);
    for( i=0; i<N; i++ ) {
        scanf("%d", &X);
        Tmp = Find(BST, X);
        if (Tmp == NULL) printf("%d is not found\n", X);
        else {
            printf("%d is found\n", Tmp->Data);
            if (Tmp==MinP) printf("%d is the smallest key\n", Tmp->Data);
            if (Tmp==MaxP) printf("%d is the largest key\n", Tmp->Data);
        }
    }
    scanf("%d", &N);
    for( i=0; i<N; i++ ) {
        scanf("%d", &X);
        BST = Delete(BST, X);
    }
    printf("Inorder:"); InorderTraversal(BST); printf("\n");

    return 0;
}
/* 你的代码将被嵌在这里 */

// From https://blog.csdn.net/qq_39339575/article/details/89852668
void InorderTraversal( BinTree BT ){
    stack<BinTree> s;
    s.push(BT);
    while(s.size() > 0){
        BinTree temp = s.top();
        while(temp) {temp = temp->Left; s.push(temp);}
        s.pop();
        if(s.size() > 0){
			temp = s.top();
		    s.pop();
            cout  <<  temp->Data << " ";
            s.push(temp->Right);
        }
    }
}
void PreorderTraversal( BinTree BT ){
    stack<BinTree> s;
    s.push(BT);
    while(s.size() > 0){
        BinTree temp = s.top();
        while(temp) {   cout <<( temp->Data)<< " ";  temp = temp->Left;  s.push(temp);}
        s.pop();
  
        if(s.size() > 0){
			temp = s.top();
			s.pop();
            s.push(temp->Right);
        }
    }
}
BinTree Insert( BinTree BST, ElementType X )
{
    if(BST == NULL)
    {
         BST = (BinTree)malloc(sizeof(struct TNode));
         BST->Data = X;
         BST->Left = NULL;
         BST->Right = NULL;
    }
    else
    {
        if(BST->Data > X)
            BST->Left = Insert(BST->Left, X);
        else
            BST->Right = Insert(BST->Right, X);
    }
    return BST;
}
BinTree Delete( BinTree BST, ElementType X )
{
    BinTree Tmp;
    if(!BST)
        printf("Not Found\n");
    else
    {
        if(BST->Data > X)
            BST->Left = Delete(BST->Left, X);
        else if(BST->Data < X)
            BST->Right = Delete(BST->Right, X);
        else
        {
            if(BST->Left && BST->Right)
            {
                Tmp = FindMin(BST->Right);
                BST->Data = Tmp->Data;
                BST->Right = Delete(BST->Right, Tmp->Data);
            }
            else
            {
                Tmp = BST;
                if(Tmp->Right)
                    BST = BST->Right;
                else
                    BST = BST->Left;
                // free(Tmp);
            }
        }
    }
    return BST;
}
BinTree Find( BinTree BST, ElementType X )
{
    if(BST==NULL)
        return NULL;
    else if(BST->Data > X)
        BST = Find(BST->Left, X);
    else if(BST->Data < X)
        BST = Find(BST->Right, X);
    else
        return BST;
}
BinTree FindMin( BinTree BST )
{
    if(BST==NULL)
        return BST;
    while(BST->Left!=NULL)
        BST = BST->Left;
    return BST;
}
BinTree FindMax( BinTree BST )
{
    if(BST==NULL)
        return BST;
    while(BST->Right!=NULL)
        BST = BST->Right;
    return BST;
}

单独插入疑惑点

主要是和不typedef struct A *Btypedef struct A B,此处感谢CJT同学的回复

首先提出问题的原因是我看到课程样例对于typedef都是第一种模式,也就是typedef struct TNode *BinTree;

然后我就通过万能的必应搜了一下C/C++语法知识:typedef struct 用法详解_Shirven-CSDN博客

然后就是更疑惑了 为啥呢?带*和不带都可以呢,然后CJT同学上场:一种可以直接设变量,一种可以用指针
变量的话赋值不能直接将整个结构体赋值… 然后我就觉得 那为啥要前者呢直接不用不是更好吗 大家都走指针 走地址 [因为前者可以相对赋值,比如你弄个局部变量,你就可以把这个变量拿出来,**单独**对成员变量赋值,然后单独去将它拿出来]

然后如果用前者的话使用方式应该是 tagMyStruct.a
后者的话是pNode->pNext

然后就设计到关于.->的区别了

截图于C++中::和:, .和->的作用和区别? - 知乎 (zhihu.com)

关于结构体:我看C++ Primer书上P74写了 C++中允许在申明结构变量时省略关键字struct 所以使用.方式也就不用typedef

第五周:堆

优先队列的完全二叉树去表示 综合来看更好效率的删除 插入 查找操作

堆的两个特性:

  • 结构性:用数组表示的完全二叉树

  • 有序性:任一结点的关键字是其子树所有结点的最大值(或最小值)

    “最大堆” 也称 “大顶堆”:最大值

    ”最小堆“ 也称 “小顶堆”:最小值

编程作业

  1. 堆中的路径 将在“小白专场”中介绍C语言的实现方法,是建立最小堆的基本操作训练,一定要做;
  2. File Transfer 关于并查集,2005、2007年浙江大学计算机学院免试研究生上机考试题即由此题改编而来。“小白专场”中介绍了原始并查集算法的优化,听完课以后自己尝试一下;
  3. Huffman Codes 考察对Huffman编码的理解,程序可能略繁,量力而为。

05-树7 堆中的路径

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define MINH -10001 //数字区间是[-10000,10000]
int H[1001], size;
void MakeHeap();
void Insert(int num);
int main()
{
    int N, M, input;
    MakeHeap();
    scanf("%d %d", &N, &M);
    for (int i = 0; i < N;i++)
    {
        scanf("%d", &input);
        Insert(input);
    }
    for (int i = 0; i < M; i++)
    {
        scanf("%d", &input);
        printf("%d", H[input]);//先把自己打印出来
        while(input>1)//往上找他的父节点
        {
            input /= 2;
            printf(" %d", H[input]);
        }
        printf("\n");
    }
    return 0;
}
void MakeHeap()
{
    size = 0;
    H[size] = MINH;
    //设置岗哨
}
void Insert(int num)
{
    int i;
    for (i = ++size; H[i / 2] > num;i/=2)
        H[i] = H[i / 2];
    H[i] = num;
}

05-树8 File Transfer

这一题在小白专场里讲的很仔细了,主要是拿数组和原先设的-1点合理利用,比如数组的下标是所输入数字本身,然后数组里的数据是这个数字指向的parent (我感觉这样的"优秀"操作似乎似曾相识 在 02-线性结构3 Reversing Linked List

一开始看完小白专场后,觉得 势在必得 肯定能行,然而翻车现场:

  1. 使用Find的时候 忘记注意,输入的是数组下标 需要比原数字-1的!
  2. 输入数字的范围是 2 ≤ N ≤ 1 0 4 2\le N \le 10^4 2N104 然而一开始我把数组的MAXSIZE 设为了1000 emmm

以下为运行通过的代码:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAX_SIZE 10000
typedef int SetType[MAX_SIZE]; //use data=parent's label; use label=self data
void InsertNum(SetType S);
void InitData(SetType S, int n);
void Union(SetType S, int n1, int n2);
int Find(SetType S, int n);//return parent
void Check(SetType S);
int main()
{
    int Total_num;
    SetType S;
    char label = ' ';
    scanf("%d", &Total_num);
    InitData(S, Total_num);
    while (label != 'S')
    {
        scanf("%c", &label);
        switch (label)
        {
        case 'I':
            InsertNum(S);
            break;
        case 'C':
            Check(S);
            break;
        default:
            break;
        }

    } 
    int count;// count num components
    for (int i = 0; i < Total_num; i++)
    {
        if(S[i]<0)
            count++;
    }
    if(count == 1)
        printf("The network is connected.\n");
    else
        printf("There are %d components.\n", count);

    return 0;
}
void InitData(SetType S, int n)
{
    for (int i = 0; i < n;i++)
        S[i] = -1;
}
void InsertNum(SetType S)
{
    int c1, c2, Root1, Root2;
    scanf("%d %d\n", &c1, &c2);
    Root1 = Find(S, c1-1);
    Root2 = Find(S, c2-1);
    if(Root1 != Root2)
        Union(S, Root1, Root2);
}
int Find(SetType S, int n)
{
    if(S[n]<0)
        return n;
    else
        return S[n] = Find(S, S[n]);
}
void Union(SetType S,int n1, int n2)
{
    if (S[n1]<S[n2])
    {
        S[n1] += S[n2];
        S[n2] = n1;
    }
    else
    {
        S[n2] += S[n1];
        S[n1] = n2;
    }
    
}
void Check(SetType S)
{
    int c1, c2, Root1, Root2;
    scanf("%d %d\n", &c1, &c2);
    Root1 = Find(S, c1-1);
    Root2 = Find(S, c2-1);
    if(Root1 == Root2)
        printf("yes\n");
    else
        printf("no\n");
}

05-树9 Huffman Codes

emmm 基本从后面的输入到构造Huffman Tree后再到求WPL都可以,就是前半段输入数组的过程 构造Huffman和求WPL不太行,虽然大概知道思路,但是感觉好多东西又要排序(小的在前)然后又要删除合并,没有想到很好的求WPL的方式所以就在这个部分求助了一下百度,C++果然是香啊… 羡慕竟然有priority_queue这种好东西 emmm vector我很久之前就知道了他是个好东西 但是为了意义上的锻炼 不到万不得已 一般都不会用C++ 对基础的数据结构来看总有种作弊的感觉 hhh。(我是认真的… 我确实一直没怎么研究过C++的库到底能干多少东西)

这道题从开写 构思各个部分实现什么 再到求助 再到写完 又是一个三小时 emmm 啥时候才能搞快点噢

这里曾经找过一个问题,当时Insert函数就给else加了return 导致一直上传的时候片段错误(但实际在自己电脑运行的时候没有报错… 我也不知道为啥,当然结尾加了后才是完整的,迭代迭的自己给忘了

#include <iostream>
#include <queue>

#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
using namespace std;

typedef struct TreeNode *Tree;
struct TreeNode {
    int v;
    Tree Left, Right;
};

Tree Insert(Tree T, char label, char *code);
int CoumputeWPL(int f[],int size);

int main()
{
    int N, M, f[65], RightWPL, NowWPL=0;
    Tree Huffman = NULL;
    char c[65];
    scanf("%d\n", &N);

    for (int i = 0; i < N;i++)
    {
        scanf("%c %d ", &c[i], &f[i]);
    }
    RightWPL = CoumputeWPL(f,N);
    scanf("%d\n", &M);
    char label;
    char code[65];
    //int char_i = 0;//if the input rank is not ABCDEFG
    for (int j = 0; j < M;j++)
    {
        NowWPL = 0;
        for (int i = 0; i < N;i++)
        {
            scanf("%c ", &label);
            scanf("%s\n", code);
            //do something construct Huffman codes
            Huffman = Insert(Huffman, label, code);
            NowWPL += f[i] * (Huffman->v-1);
        }

        Huffman = NULL;
        //check the WPL value if equal to prior one
        if(RightWPL == NowWPL)
            printf("Yes\n");
        else
            printf("No\n");
    }
        return 0;
}

/* ------------- Reference: https://blog.csdn.net/VVVLeHr/article/details/87877439 BELOW -------- */
int CoumputeWPL(int f[],int size)
{
    int result=0;
    //greater: 升序队列,小顶堆
    priority_queue<int, vector<int>, greater<int>> q;
    for (int i = 0; i < size;i++)
        q.push(f[i]);
    while(q.size()>=2)
    {
        int a = q.top();
        q.pop();
        int b = q.top();
        q.pop();
        q.push(a + b);
        result += (a + b);
    }
    return result;
}
/* ------------- Reference: https://blog.csdn.net/VVVLeHr/article/details/87877439 UP -------- */


Tree Insert(Tree T, char label, char *code)
{
    int num;
    if(T==NULL)
    {
        T = (Tree)malloc(sizeof(struct TreeNode));
        T->Left = NULL;
        T->Right = NULL;
        T->v = 0;
    }
    if (*code == '0')
    {
        T->Left = Insert(T->Left, label, ++code);
        num = T->Left->v;
    }

    else if(*code == '1')
    {
        T->Right = Insert(T->Right, label, ++code);
        num = T->Right->v;
    }
    else
    {
        T->v = T->v + 1;
        return T;
    }
    T->v = num + 1;
    return T;
}

我重新听课的时候发现我根本没考虑… 放入的是叶子节点的问题(就是如果放在根节点 我也继续放了… 看来是WPL那个案例没有完全等 所以过了?),也就是说我可能放在根节点上了,我也不知道为啥我的测试案例过了 emmm

也就是说本身Huffman Codes应该满足:

  1. 最优编码 —— 总长度(WPL)最小
  2. 无歧义编码 —— 前缀码:数据仅存在于叶子结点
  3. 没有度为1的节点 —— 满足1、2则必然有3

第六周:图

内心OS: !终于 我进度过半了 虽然我觉得我刷完可能秋招已经结束了 😵 好了 进入正题

这个部分的图,我似乎好像真的有那么点熟悉(主要图形学和python那个networkx库 6010课 等一系列课程中都多多少少介绍过这个概念),所以笔记可能显得非常少

程序中表示图的方式:邻接矩阵 G [ N ] [ N ] G[N][N] G[N][N] N N N​个顶点从0到 N − 1 N-1 N1​ 编号

讨论6.1 - 关于用链表得到有向图的出、入度–其他办法

关于用链表表示图并且能方便地得到有向图的出、入度,你有什么其他的办法吗? 在评论区看到几个挺好的:

  1. 用树表示更好,顶点做根,左枝是出度,右枝是入度。

  2. 修改Node的结构,多创建一个域,1表示入,0表示出,这样就能知道该节点到底是入还是出

  3. 修改链表的整体结构,但是这个我觉得并没有那么容易,还没想出来。但是十字链表的确可以把邻接表和逆邻接表串起来

然后十字链表新概念,查一下后

编程作业

  1. 列出连通集 非常基础的训练,一定要做;
  2. Saving James Bond - Easy Version 可怜的007在等着你拯救,你……看着办哈;
  3. 六度空间 在听完课以后,这题的思路应该比较清晰了,不过实现起来还是颇有码量的,有时间就尝试一下。

06-图1 列出连通集

写完了 最近好多活 杂七杂八 这个在多次脑子切换间写出来的 一开始 第1个是单独点,最大N 这个案例一直过不了,后面搜也没搜出什么,但是搜到了 有人用C拿数组模拟队列的 感觉很聪明的做法哎,当然 我比较菜 所以我用了C++。对了前面提到的那个案例,后面找到原因了是因为

  1. 建邻接表的时候没有排序,这次在里面判断了每次的排序
  2. BFS的逻辑在一开始跳过了头的邻接点下标

然后怎么发现的呢?一步步调试 emmm 然后自己输入了一个这个的 案例:

10 9
1 8
1 4
1 9
2 8
7 8
4 2
3 2
5 4
6 7

#include <iostream>
#include <queue>
using namespace std;

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define MaxVertexNum 100

typedef struct AdjVode *PtrToAdjVNode;
struct AdjVode {
    int AdjV;//邻接点下标
    PtrToAdjVNode Next;
};
bool V_visit[MaxVertexNum];
typedef struct Vnode{
    PtrToAdjVNode FirstEdge;
} AdjList[MaxVertexNum];

typedef struct GNode *PtrToGNode;
struct GNode{
    int Nv;
    int Ne;
    AdjList G; //邻接表
};
typedef PtrToGNode LGraph;
void DFS_Connect(LGraph G, int vertex_i);
LGraph CreateGraph(int N);
LGraph Insert(LGraph G, int v1, int v2);
void BFS_Connect(LGraph G, int vertex_i);
int main()
{

    int N, E;
    int v1, v2;
    LGraph G;
    scanf("%d %d", &N, &E);
    G = CreateGraph(N);
    for (int i = 0; i < E;i++)
    {
        scanf("%d %d", &v1, &v2);
        G = Insert(G, v1, v2);
    }
    //DFS print
    for (int i = 0; i < G->Nv;i++)
    {
        if(!V_visit[i])
        {
            printf("{ ");
            printf("%d ", i);
            DFS_Connect(G, i);
            printf("}\n");
        }
            
    }
    
    //BFS print
    for (int i = 0; i < G->Nv;i++)
    {
        V_visit[i] = false;
    }
    
    for (int i = 0; i < G->Nv;i++)
    {
        if(!V_visit[i])
        {
            printf("{ ");
            printf("%d ", i);
            BFS_Connect(G, i);
            printf("}\n");
        }

    }
    
    return 0;

}
void DFS_Connect(LGraph G,int vertex_i)
{
    V_visit[vertex_i] = true;
    PtrToAdjVNode W;
    for (W = G->G[vertex_i].FirstEdge; W; W = W->Next)
        if(!V_visit[W->AdjV])
        {
            printf("%d ", W->AdjV);
            DFS_Connect(G, W->AdjV);
        }
}
queue<PtrToAdjVNode> myqueue;
void BFS_Connect(LGraph G,int vertex_i)
{
    V_visit[vertex_i] = true;
    PtrToAdjVNode W;
    for (W = G->G[vertex_i].FirstEdge; W; W = W->Next)
        myqueue.push(W);
    while(!myqueue.empty())
    {
        W = myqueue.front();
        if(W==NULL)
        {
            myqueue.pop();
            continue;
        }
        vertex_i = W->AdjV;
        myqueue.pop();
        if(!V_visit[vertex_i])
        {
            printf("%d ", vertex_i);
            V_visit[vertex_i] = true;
            while(W)
            {
                
                if(W!=NULL)
                {
                    vertex_i = W->AdjV;
                    for (W = G->G[vertex_i].FirstEdge; W; W = W->Next)
                        myqueue.push(W);
                }

            }
        }
    }

}
LGraph CreateGraph(int N)
{
    LGraph G;
    G = (LGraph)malloc(sizeof(struct GNode));
    G->Nv = N;
    G->Ne = 0;
    for (int i = 0; i < G->Nv;i++)
        G->G[i].FirstEdge = NULL;
    return G;
}
LGraph Insert(LGraph G, int v1, int v2)
{
    PtrToAdjVNode NewNode;
    PtrToAdjVNode W,W_next;
    int temp;
    NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVode));
    NewNode->AdjV = v2;
    V_visit[v1] = false;
    
    NewNode->Next = G->G[v1].FirstEdge;
    G->G[v1].FirstEdge = NewNode;

    for (W = G->G[v1].FirstEdge; W; W = W->Next)
    {
        W_next = W->Next;
        if(W_next==NULL)
            break;
        else
        {
            if(W->AdjV > W_next->AdjV)
            {
                temp = W_next->AdjV;
                W_next->AdjV = W->AdjV;
                W->AdjV = temp;
            }
        }
    }


    //无向图还需在v2,v1
    NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVode));
    NewNode->AdjV = v1;
    V_visit[v2] = false;

    NewNode->Next = G->G[v2].FirstEdge;
    G->G[v2].FirstEdge = NewNode;

    for (W = G->G[v2].FirstEdge; W; W = W->Next)
    {
        W_next = W->Next;
        if(W_next==NULL)
            break;
        else
        {
            if(W->AdjV > W_next->AdjV)
            {
                temp = W_next->AdjV;
                W_next->AdjV = W->AdjV;
                W->AdjV = temp;
            }
        }
    }


    return G;
}

06-图2 Saving James Bond - Easy Version

这个真是绝了,一开始一直过不了**最小跳,人工乱序**,然后我搜了一圈没啥感觉,然后仔细检查发现

  1. 自己最后一个判断IsSafe的等于号少了(我一开始都写成了小于号,没注意到等于号 后面再次读题的时候发现的)
  2. 第二个是DFS内的for循环需要i从0-N,一开始写成了vertex_i

emmmm 我没找两天 就找了10分钟hhh 我搜的时候发现有个仁兄找了两天 hhhh 同道中人啊

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define MaxVertexNum 100


typedef struct Vertex *PtrToV;
struct Vertex{
    int x;
    int y;
    bool visit;
};

void CheckJump(int D, int N, PtrToV *vertex);
bool FirstJump(int x, int y, int D);
bool DFS(int x, int y, PtrToV *vertex, int D, int N, int vertex_i);
bool Jump(int now_x, int now_y, int next_x, int next_y, int D);
bool IsSafe(int x, int y, int D);
int main()
{

    int N, D;
    int x_, y_;//location of a crocodile
    scanf("%d %d", &N, &D);
    PtrToV vertex[MaxVertexNum];
    for (int i = 0; i < N;i++)
    {
        scanf("%d %d", &x_, &y_);
        vertex[i] = (PtrToV)malloc(sizeof(struct Vertex));
        vertex[i]->x = x_;
        vertex[i]->y = y_;
        vertex[i]->visit = false;
    }
    CheckJump(D, N, vertex);

    return 0;

}
void CheckJump(int D, int N, PtrToV *vertex)
{
    int x_, y_;
    bool result;
    // N = N + 2;
    for (int i = 0; i < N;i++)
    {
        x_ = vertex[i]->x;
        y_ = vertex[i]->y;
        if (!vertex[i]->visit && FirstJump(x_, y_, D))
        {
            vertex[i]->visit = true;
            result = DFS(x_, y_, vertex, D, N, i);
            if (result == true)
                break;
        }

    }
    if (result == true)
        printf("Yes");
    else
        printf("No");
}
bool DFS(int x, int y, PtrToV *vertex, int D, int N, int vertex_i)
{

    bool result = false;
    if (IsSafe(x, y, D))
        return true;
    else
    {
        for (int i = 0; i < N; i++)
        {
            if (!vertex[i]->visit && Jump(x, y, vertex[i]->x, vertex[i]->y, D))
            {
                vertex[i]->visit = true;
                result = DFS(vertex[i]->x, vertex[i]->y, vertex, D, N, i);
                if (result == true)
                    break;
            }
        }
    }
    return result;
}
bool Jump(int now_x, int now_y, int next_x, int next_y, int D)
{
    if ((now_x - next_x) * (now_x - next_x) + (now_y - next_y) * (now_y - next_y) <= D * D)
        return true;
    else
        return false;
}
bool FirstJump(int x, int y, int D)
{
    if ((x * x + y * y) <= (15 + D) * (15 + D))
        return true;
    else
        return false;
}
bool IsSafe(int x, int y, int D)
{
    if (abs(abs(x) - 50) <= D || abs(abs(y) - 50) <= D)
        return true;
    else
        return false;
}

06-图3 六度空间

这个题起码加起来的debug时间估计达到3小时了,主要是一开始自己写的 死活在深入算level的时候多算一层(然后这个debug和大规模修改函数基本上达到了三次还是没能改出来),导致输出的时候有错误。后面受不了了 想不出什么好办法就直接照着课上的伪代码 一句句的去实现,然而我还在看为什么输出还是错误 郁闷了半小时,最终发现%2f不是输出小数点后两位,得加个%0.2f 我真是… 当时 emmm 啊

大致原因:

  1. 根本上来讲,可以看出 伪代码的实现是一次性看完所有BFS 而我的伪的 也就是看一次 OK 判断一次tail
  2. 输出小数点后两位记错了%0.2f
  3. 在输入第一个点时,一开始的做法是先进行BFS 而不是像伪代码那里直接将第一个点加入队列统一处理
  4. 前面reset visit矩阵和reset queue队列都忘了 emmm 问就是debug d出来的 发现哦吼 后面咋都是0,100%这种奇怪的数字
#include <iostream>
#include <queue>
using namespace std;

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define MaxVertexNum 1000
typedef struct AdjVnode *PtrToAdjVNode;
struct AdjVnode
{
    int AdjV;
    PtrToAdjVNode Next;
};

typedef struct Vnode{
    PtrToAdjVNode Next;
} AdjList[MaxVertexNum];
typedef struct GNode *PtrToGraph;
struct GNode
{
    int Nv;
    int Ne;
    AdjList G;
};
typedef PtrToGraph LGraph;

LGraph CreateGraph(int N);
LGraph Insert(LGraph G, int v1, int v2);
bool V_visit[MaxVertexNum];
int BFS(LGraph G, int vertex_i);
queue<PtrToAdjVNode> clear_queue(queue<PtrToAdjVNode> q);
void reset_visit(int N);
int main()
{
    int N, M;
    int v1, v2;
    int count;
    float result;
    scanf("%d %d", &N, &M);
    LGraph G;
    G = CreateGraph(N);
    for (int i = 0; i < M;i++)
    {
        scanf("%d %d", &v1, &v2);
        v1 = v1 - 1;
        v2 = v2 - 1;
        G = Insert(G, v1, v2);
    }
    reset_visit(N);
    for (int i = 0; i < N;i++)
    {
        count = BFS(G, i);
        reset_visit(N);
        result = 1.0 * count / G->Nv;
        printf("%d: %0.2f%%\n", i+1, result * 100);
    }
    return 0;
}

queue<PtrToAdjVNode> myqueue;
int BFS(LGraph G,int vertex_i)
{
    V_visit[vertex_i] = true;
    int count = 1;//for vertex number less than 6 distance
    int level = 0;
    int last = vertex_i;
    int tail; //tail for next layer

    // Enqueue(V,Q);
    PtrToAdjVNode W;
    W = (PtrToAdjVNode)malloc(sizeof(struct AdjVnode));
    W->AdjV = vertex_i;
    W->Next = G->G[vertex_i].Next;
    myqueue.push(W);
    
	//断连与myqueue中的地址 清空
    W = (PtrToAdjVNode)malloc(sizeof(struct AdjVnode));
    while(!myqueue.empty())
    {
        //Dequeue(Q)
        vertex_i = myqueue.front()->AdjV;
        myqueue.pop();
        
        //循环vertex_i的每个邻接点
        for (W = G->G[vertex_i].Next; W; W = W->Next)
        {
            if(!V_visit[W->AdjV])
            {
                V_visit[W->AdjV] = true;
                myqueue.push(W);
                count++;
                tail = W->AdjV;
            }
        }
        if(vertex_i == last)
        {
            level++;
            last = tail;
        }
        if (level == 6)
            break;
    }
    myqueue = clear_queue(myqueue);
    return count;
}
void reset_visit(int N)
{
    for (int j = 0; j < N;j++)
    {
        V_visit[j] = false;
    }
}
queue<PtrToAdjVNode> clear_queue(queue<PtrToAdjVNode> q)
{
    queue<PtrToAdjVNode> empty;
    swap(empty, q);
    return q;
}
LGraph CreateGraph(int N)
{
    LGraph G;
    G = (LGraph)malloc(sizeof(struct GNode));
    G->Nv = N;
    G->Ne = 0;
    for (int i = 0; i < G->Nv;i++)
        G->G[i].Next = NULL;
    return G;
}
LGraph Insert(LGraph G, int v1, int v2)
{
    PtrToAdjVNode NewNode;
    NewNode = (PtrToAdjVNode)malloc(sizeof(struct Vnode));
    NewNode->AdjV = v2;
    NewNode->Next = G->G[v1].Next;
    G->G[v1].Next = NewNode;


    //无向图还需在v2,v1
    NewNode = (PtrToAdjVNode)malloc(sizeof(struct Vnode));
    NewNode->AdjV = v1;
    NewNode->Next = G->G[v2].Next;
    G->G[v2].Next = NewNode;

    return G;
}

我不甘心 又分析了一遍 我写的和伪代码,发现emmm 判断的问题,就是我是一个点进来了 好设true然后把他的周围压入队列;而伪代码是一个点进来了 好把他周围的都设true同时入队;这样看来 主要在设true环节,后者的做法就可以避免之前提到的level多加了一层,在碰到顶端的时候 回去看点设true 然后level多加了一个;看看后面能不能再写一遍 加深一下印象点(所以我前面连通集到底是怎么给过的 看来是没走到layer level所以没注意到这点不同,点都是正常读取存入之类的【对比06-图1和06-图3的代码就能发现了】

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kin-Zhang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值