树、二叉树与森林的转换 1.树转换为二叉树上图是一个度为3的树的结构 ,转换为二叉树分为加线、抹线和旋转几个过程:①加线:在所有兄弟节点间加一条线②抹线:只保留他与第一个孩子结点间的连线,删除它与其它孩子结点间的连线③旋转:以树的根节点为轴心,将整棵树顺时针旋转一定角度使结构层次分明2.森林转换为二叉树森林是由若干棵树组成的,可以将森林中每根树的根节点看作是兄弟节点,由于每棵树都可转换为二叉树,故森林也可转换为二叉树。下图是一个森林:①把每棵树转换为二叉树②第一棵二叉树不动,从第二棵
数据结构————KMP算法 KMP算法相比于BF算法,是一种用空间换时间的算法,比BF的普通算法效率高很多。BF算法需要将源字符串中的每一个字符与模式串中字符比较至少一次,并且会有大量回溯的时间,下面我们来分析一下BF算法的时间复杂度:我们假设源字符串的长度为n,模式串的长度为m,假设从源字符串的第i个位置开始于模式匹配成功。BF算法的时间复杂度分两种情况,第一种是最好情况源字符串中匹配位置之前的每一个字符都与模式串的第一个字符不一样,第二种是最坏情况即源字符串中匹配位置之前的每一组都只与模式串中的最后一个字符不一样。1.
数据结构————串模式匹配(BF) 字符串的模式匹配是将两个A和B字符串进行比较,如果A字符串是B的子串,那么返回B在A串中出现的位置;如果不是子串,则返回错误。那么BF算法就是最好想的算法,但是时间复杂度就会大一点。设置两个指针分别指向A和B字符串,如果A指针指向的内容和B指针指向的内容相同的话,A指针和B指针都向后移动并依次比较;如果A指针指向的内容和B指针指向的内容不同的话,A指针向后移动,B指针不发生变动再依次进行比较。下面我们来实现这个算法:#include<stdio.h>#include<std
数据结构————链队讲解 链队就是将队列的元素用链表的形式来表示,设置头节点和尾结点来控制队列的尾入头出。下面我们主要来实现链队的基本操作:1.头文件#include<stdio.h>#include<stdlib.h>#include<assert.h>#include<stdbool.h>typedef int DataType;typedef struct QueueNode{ struct QueueNode*next; DataType
数据结构————循环队列讲解 队列的基本概念队列与栈的不同之处就在于栈是FILO(first in last out)即先进后出,而队列是FIFO(first in first out)即先进先出。队列可应用于银行取号,先取号的要排在后取号的前面来先办理业务,符合先进先出的原则;而栈可应用于QQ消息界面,即最顶层消息栏是最近一次的消息,越往下消息的发布时间越晚,符合先进后出的原则。对于栈来说,栈一般只有一个栈顶指针top来控制元素的进栈和出栈,因为栈只有一个栈顶一个地方来进出;而队列有队头和队尾两个指针来控制元素的入队和出队,
数据结构————括号的匹配(栈) 如何进行括号的匹配呢?比如说输入字符串:"{{([[()]])}}"可以看出该字符串是完全对称的,那么这个就是匹配的括号,如果输入:"{{(([)]]}",该字符串不是对称的,那么就不匹配。思路就是将所有左括号依次入栈,之后再依次出栈与右括号匹配,如果匹配就指向下一个右括号并且将下一个元素出栈。直到栈顶元素为空或字符串为空。下面我们先来实现栈的基本操作:【头文件】#include<stdio.h>#include<stdlib.h>#include<ass
数据结构————不带头单链表操作讲解 我们先来实现不带头单链表的基本操作:头文件test.h:#include<stdio.h>#include<stdlib.h>typedef int SLTDataType;struct SListNode{ SLTDataType data; struct SListNode*next;};typedef struct SListNode SLTnode;void InitSlist(SLTnode*head);//初始化void Inser
数据结构————倒置带头结点单链表操作讲解 我们先来实现带头单链表的基本操作:头文件:#include<stdio.h>#include<assert.h>#include<stdlib.h>typedef int ElemType;typedef struct LNode{ ElemType data; struct LNode*next;}LT,*link;void InitList(Link phead);//初始化void InsertListfront(Link p
经典笔试真题讲解1 题目一:void GetMemory(char*p){ p=(char*)malloc(100);}void Test(void){ char*str=NULL; GetMemory(str); strcpy(str,"hello world"); printf(str);}请问运行Test函数会有什么样的结果?答案:非法访问【解析】进入Test函数之后,先初始化了str的指针指向NULL,后调用了GetMemory函数,跳出函数后,进行字符串
动态内存管理 动态内存是什么?动态内存就是动态开辟的内存,相对于数组来说,数组开辟内存时要先定义大小,有时候我们用不了那么多空间就会浪费,并且我们需要的空间大小有时候只有在程序运行起来时才知道。而动态开辟的内存空间用多少开辟多少,方便而且不占内存。下面我们讲讲动态内存函数:malloc函数形式:void*malloc(size_t size)功能:该函数向内存申请一块连续可用的空间,并返回指向这块空间的指针(首地址)1.如果开辟成功,返回一个开辟空间的指针2.如果开辟失败,返回一个NULL指
多项式求值——第一个方法:两个栈 多项式求值的第一个方法的基本思路就是:设定两个栈,一个栈放数字,另一个栈放符号。将表达式输入到字符数组中,如果指针指向符号,并且该符号的优先级大于符号栈顶的优先级,那么就放进符号栈中;如果指针指向的符号的优先级小于符号栈栈顶的优先级,那么将数字栈中pop出两个数字和符号栈中pop出的一个字符进行运算,将运算的结果放进数字栈中;如果指针指向数字,那么就将数字放入数字栈中,再将指针指向下一个字符。之后当字符数组全部输出完之后,就可以返回数字栈的栈顶元素了。那么我们的头文件就是栈的基本操作:#inclu
c语言讲解------内存函数 了解了字符串函数后我们了解在内存中操作的函数,就叫内存函数。memcpy函数从source位置开始向后复制num个字节的数据到destination的内存位置形式:void*memcpy(void*destination,const void*source,size_t num)1.这个函数在遇到\0时不会停下来2.如果source和destination有任何的重叠,复制的结果都是未定义的下面我们来使用:#include<stdio.h>#include&l
字符串与内存函数 strlen函数:求字符串长度形式:size_t strlen(const char*str)1.strlen会在遍历到'\0‘结束,故字符数组或字符串中必须有\0或定义好字符数组的长度2.strlen返回的字符串或字符数组长度不包含\03.strlen返回的是无符号类型,所以不可以相减(相减后结果仍然是无符号的)下面我们自主实现一个mystrlen函数,共有三种实现方法:①计数器实现size_t mystrlen(const char*str){ assert(s
堆栈的基本操作(C语言实现) 堆栈就是栈通过数组的形式实现,我们都知道栈符合FILO即First In Last Out先进后出、后进先出的原则。栈顶指针top如果赋值-1,那么top指向栈顶元素;如果top赋值0,那么top指向栈顶上方的元素。下面我们来实现堆栈的基本操作(附有其他操作):1.初始化#include<stdio.h>#include<assert.h>#include<stdlib.h>typedef int STDataType;typedef struct St
指针————sizeof/strlen中参数含义 这篇文章主要总结了一系列的sizeof和strlen常见的参数含义。一、数组int a[]={1,2,3,4};1.sizeof(a);//4*4=16bit2.sizeof(a+0);//4/8bit3.sizeof(*a);//4bit4.sizeof(a+1);//4bit5.sizeof(a[1]);//4bit6.sizeof(&a+1);//4/8bit7.sizeof(&a[0]);//4/8bit8.sizeof(&a[0]+1);//4/8
qsort函数讲解 qsort函数的作用是将所有数据排序,那么它和普通的冒泡排序或者选择排序有什么区别呢?它不仅仅可以排序数组中的数字,还可以排序结构体。当然升序和降序它都支持,不过输入参数的顺序会有所不同,下面我们来详细讲解一下这个函数。首先,我们先看一下库函数中它的使用方法:可以看出qsort的返回值有三种情况:如果p1的值小于p2,那么返回<0;如果p1的值大于p2;那么返回>0;如果p1的值等于p2,那么返回=0。由此可见,如果我们想排序成升序的话,就按照p1,p2的顺序传值,如果我们想排序成降
回调函数讲解 首先,回调函数的定义就是指通过函数指针调用的函数。之前我们讲过函数指针是指向函数的指针。其表示形式为:void(*)()那么我们通过一个简单的代码来实现回调函数:void test(){ printf("CSDN");}void print(void(*p)()){ if(1) p();}int main(){ print(test); return 0;}这时,其中print(test)就是回调函数,他是通过取出test函
函数指针与函数指针数组讲解 函数指针函数指针指的就是指向函数的指针,表示形式为void (*)(),其中第一个括号中放的是指针变量,第二个括号放的是参数的类型和数目,如若某函数形参变量是int Add(int a,int b),那么就是int(*pf)(int,int),编译器定义int(pf)(int,int)的形式也同样成立。其中函数的地址传址加不加取地址符都是可以的,如int(*pf)(int,int)=Add; 或 int(*pf)(int,int)=&Add;那么我们如何应用呢,下面有两个简单的示例:
数据结构—————顺序表讲解 表现形式顺序表主要的表现形式就是数组的形式链表主要表现形式是指针的形式存储方式顺序表的存储形式是随机存储链表的存储形式则是顺序存储所以对于顺序表而言,链表在中间的插入和删除的操作时间复杂度为O(n),而顺序表插入和删除操作的时间复杂度为O(n^2);对于链表而言,查找时需要从头遍历一遍链表,来找到对应的元素,而顺序表可以直接找到对应元素或位置,不需要遍历。基本操作顺序表的基本操作有初始化、前插、后插、前删、后删、随机插入、随机删除等。1.初始化2.前插3.后插
数据结构————带头结点单链表讲解 单链表分为带头结点的和不带头结点的,两者的根本区别在于是否有不存储元素的头结点。头结点的存在可以让链表为空的时候不销毁,操作也更方便,不用附带二级指针。表示形式每一个结点都有指针域和数据域,指针域用来指向下一个结点,数据域用来存放该结点的元素。基本操作单链表的基本操作有初始化、前插、后插、前删、后删等。后面我们将讲述基于这些基本操作上更复杂的操作。1.初始化2.前插3.后插4.前删5.后删6.插入7.删除...