超硬核十万字!全网最全 数据结构 代码,随便秒杀老师/面试官,我说的
Posted 兔老大RabbitMQ
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了超硬核十万字!全网最全 数据结构 代码,随便秒杀老师/面试官,我说的相关的知识,希望对你有一定的参考价值。
本文代码实现基本按照《数据结构》课本目录顺序,外加大量的复杂算法实现,一篇文章足够。能换你一个收藏了吧?
当然如果落下什么了欢迎大家评论指出
目录
问题三:怎么用对称轴向两边扩的方法找到偶回文?(容易操作的)
那么请问,加进去的符号,有什么要求么?是不是必须在原字符中没出现过?请思考
后缀树:后缀树,就是把一串字符的所有后缀保存并且压缩的字典树。
相对于字典树来说,后缀树并不是针对大量字符串的,而是针对一个或几个字符串来解决问题。比如字符串的回文子串,两个字符串的最长公共子串等等。
后缀数组:就是把某个字符串的所有后缀按照字典序排序后的数组。(数组中保存起始位置就好了,结束位置一定是最后)
输入某二叉树的后序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字
输入某二叉树的后序遍历和先序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字
https://blog.csdn.net/hebtu666/article/details/84322113
并查集入门三连:HDU1213 POJ1611 POJ2236
Abstract Self-Balancing Binary Search Tree
顺序存储线性表实现
在计算机中用一组地址连续的存储单元依次存储线性表的各个数据元素,称作线性表的顺序存储结构。
顺序存储结构的主要优点是节省存储空间,因为分配给数据的存储单元全用存放结点的数据(不考虑c/c++语言中数组需指定大小的情况),结点之间的逻辑关系没有占用额外的存储空间。采用这种方法时,可实现对结点的随机存取,即每一个结点对应一个序号,由该序号可以直接计算出来结点的存储地址。但顺序存储方法的主要缺点是不便于修改,对结点的插入、删除运算时,可能要移动一系列的结点。
优点:随机存取表中元素。缺点:插入和删除操作需要移动元素。
线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的(注意,这句话只适用大部分线性表,而不是全部。比如,循环链表逻辑层次上也是一种线性表(存储层次上属于链式存储),但是把最后一个数据元素的尾指针指向了首位结点)。
给出两种基本实现:
/*
静态顺序存储线性表的基本实现
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LIST_INITSIZE 100
#define ElemType int
#define Status int
#define OK 1
#define ERROR 0
typedef struct
ElemType elem[LIST_INITSIZE];
int length;
SqList;
//函数介绍
Status InitList(SqList *L); //初始化
Status ListInsert(SqList *L, int i,ElemType e);//插入
Status ListDelete(SqList *L,int i,ElemType *e);//删除
void ListPrint(SqList L);//输出打印
void DisCreat(SqList A,SqList *B,SqList *C);//拆分(按正负),也可以根据需求改
//虽然思想略简单,但是要写的没有错误,还是需要锻炼coding能力的
Status InitList(SqList *L)
L->length = 0;//长度为0
return OK;
Status ListInsert(SqList *L, int i,ElemType e)
int j;
if(i<1 || i>L->length+1)
return ERROR;//判断非法输入
if(L->length == LIST_INITSIZE)//判满
printf("表已满");//提示
return ERROR;//返回失败
for(j = L->length;j > i-1;j--)//从后往前覆盖,注意i是从1开始
L->elem[j] = L->elem[j-1];
L->elem[i-1] = e;//在留出的位置赋值
(L->length)++;//表长加1
return OK;//反回成功
Status ListDelete(SqList *L,int i,ElemType *e)
int j;
if(i<1 || i>L->length)//非法输入/表空
return ERROR;
*e = L->elem[i-1];//为了返回值
for(j = i-1;j <= L->length;j++)//从前往后覆盖
L->elem[j] = L->elem[j+1];
(L->length)--;//长度减1
return OK;//返回删除值
void ListPrint(SqList L)
int i;
for(i = 0;i < L.length;i++)
printf("%d ",L.elem[i]);
printf("\\n");//为了美观
void DisCreat(SqList A,SqList *B,SqList *C)
int i;
for(i = 0;i < A.length;i++)//依次遍历A中元素
if(A.elem[i]<0)//判断
ListInsert(B,B->length+1,A.elem[i]);//直接调用插入函数实现尾插
else
ListInsert(C,C->length+1,A.elem[i]);
int main(void)
//复制的
SqList L;
SqList B, C;
int i;
ElemType e;
ElemType data[9] = 11,-22,33,-3,-88,21,77,0,-9;
InitList(&L);
InitList(&B);
InitList(&C);
for (i = 1; i <= 9; i++)
ListInsert(&L,i,data[i-1]);
printf("插入完成后L = : ");
ListPrint(L);
ListDelete(&L,1,&e);
printf("删除第1个后L = : ");
ListPrint(L);
DisCreat(L , &B, &C);
printf("拆分L后B = : ");
ListPrint(B);
printf("拆分L后C = : ");
ListPrint(C);
printf("拆分L后L = : ");
ListPrint(L);
静态:长度固定
动态:不够存放可以加空间(搬家)
/*
子任务名任务:1_2 动态顺序存储线性表的基本实现
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LIST_INIT_SIZE 100
#define LISTINCREMENT 10
#define Status int
#define OVERFLOW -1
#define OK 1
#define ERROR 0
#define ElemType int
typedef struct
ElemType * elem;
int length;
int listsize;
SqList;
//函数介绍
Status InitList(SqList *L); //初始化
Status ListInsert(SqList *L, int i,ElemType e);//插入
Status ListDelete(SqList *L,int i,ElemType *e);//删除
void ListPrint(SqList L);//输出打印
void DeleteMin(SqList *L);//删除最小
Status InitList(SqList *L)
L->elem = (ElemType *)malloc(LIST_INIT_SIZE*sizeof(ElemType));//申请100空间
if(!L->elem)//申请失败
return ERROR;
L->length = 0;//长度0
L->listsize = LIST_INIT_SIZE;//容量100
return OK;//申请成功
Status ListInsert(SqList *L,int i,ElemType e)
int j;
ElemType *newbase;
if(i<1 || i>L->length+1)
return ERROR;//非法输入
if(L->length >= L->listsize)//存满了,需要更大空间
newbase = (ElemType*)realloc(L->elem,(L->listsize+LISTINCREMENT)*sizeof(ElemType));//大10的空间
if(!newbase)//申请失败
return ERROR;
L->elem = newbase;//调指针
L->listsize+= LISTINCREMENT;//新容量
for(j=L->length;j>i-1;j--)//从后往前覆盖
L->elem[j] = L->elem[j-1];
L->elem[i-1] = e;//在留出的位置赋值
L->length++;//长度+1
return OK;
Status ListDelete(SqList *L,int i,ElemType *e)
int j;
if(i<1 || i>L->length)//非法输入/表空
return ERROR;
*e = L->elem[i-1];//为了返回值
for(j = i-1;j <= L->length;j++)//从前往后覆盖
L->elem[j] = L->elem[j+1];
(L->length)--;//长度减1
return OK;//返回删除值
void ListPrint(SqList L)
int i;
for(i=0;i<L.length;i++)
printf("%d ",L.elem[i]);
printf("\\n");//为了美观
void DeleteMin(SqList *L)
//表空在Listdelete函数里判断
int i;
int j=0;//最小值下标
ElemType *e;
for(i=0;i<L->length;i++)//寻找最小
if(L->elem[i] < L->elem[j])
j=i;
ListDelete(L,j+1,&e);//调用删除,注意j要+1
int main(void)
SqList L;
int i;
ElemType e;
ElemType data[9] = 11,-22,-33,3,-88,21,77,0,-9;
InitList(&L);
for (i = 1; i <= 9; i++)
ListInsert(&L,i,data[i-1]);
printf("插入完成后 L = : ");
ListPrint(L);
ListDelete(&L, 2, &e);
printf("删除第 2 个后L = : ");
ListPrint(L);
DeleteMin(&L);
printf("删除L中最小值后L = : ");
ListPrint(L);
DeleteMin(&L);
printf("删除L中最小值后L = : ");
ListPrint(L);
DeleteMin(&L);
printf("删除L中最小值后L = : ");
ListPrint(L);
单链表不带头标准c语言实现
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。
使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。链表最明显的好处就是,常规数组排列关联项目的方式可能不同于这些数据项目在记忆体或磁盘上顺序,数据的存取往往要在不同的排列顺序中转换。链表允许插入和移除表上任意位置上的节点,但是不允许随机存取。链表有很多种不同的类型:单向链表,双向链表以及循环链表。
下面给出不带头的单链表标准实现:
定义节点:
typedef struct node
int data;
struct node * next;
Node;
尾插:
void pushBackList(Node ** list, int data)
Node * head = *list;
Node * newNode = (Node *)malloc(sizeof(Node));//申请空间
newNode->data = data; newNode->next = NULL;
if(*list == NULL)//为空
*list = newNode;
else//非空
while(head ->next != NULL)
head = head->next;
head->next = newNode;
插入:
int insertList(Node ** list, int index, int data)
int n;
int size = sizeList(*list);
Node * head = *list;
Node * newNode, * temp;
if(index<0 || index>size) return 0;//非法
newNode = (Node *)malloc(sizeof(Node)); //创建新节点
newNode->data = data;
newNode->next = NULL;
if(index == 0) //头插
newNode->next = head;
*list = newNode;
return 1;
for(n=1; n<index; n++) //非头插
head = head->next;
if(index != size)
newNode->next = head->next;
//链表尾部next不需指定
head->next = newNode;
return 1;
按值删除:
void deleteList(Node ** list, int data)
Node * head = *list; Node * temp;
while(head->next!=NULL)
if(head->next->data != data)
head=head->next;
continue;
temp = head->next;
if(head->next->next == NULL) //尾节点删除
head->next = NULL;
else
head->next = temp->next;
free(temp);
head = *list;
if(head->data == data) //头结点删除
temp = head;
*list = head->next;
head = head->next;
free(temp);
打印:
void printList(Node * head)
Node * temp = head;
for(; temp != NULL; temp=temp->next)
printf("%d ", temp->data);
printf("\\n");
清空:
void freeList(Node ** list)
Node * head = *list;
Node * temp = NULL;
while(head != NULL) //依次释放
temp = head;
head = head->next;
free(temp);
*list = NULL; //置空
别的也没啥了,都是基本操作
有些代码要分情况,很麻烦,可读性较强吧
单链表不带头压缩c语言实现
注:单追求代码简洁,所以写法可能有点不标准。
//第一次拿c开始写数据结构,因为自己写的,追求代码量少,和学院ppt不太一样。有错请指出
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct node//定义节点
int data;
struct node * next;
Node;
//函数介绍
void printlist(Node * head)//打印链表
int lenlist(Node * head)//返回链表长度
void insertlist(Node ** list,int data,int index)//插入元素
void pushback(Node ** head,int data)//尾部插入
void freelist(Node ** head)//清空链表
void deletelist(Node ** list,int data)//删除元素
Node * findnode(Node ** list,int data)//查找
void change(Node ** list,int data,int temp)//改变值
打印
void printlist(Node * head)//打印链表
for(;head!=NULL;head=head->next) printf("%d ",head->data);
printf("\\n");//为了其他函数打印,最后换行
链表长度
int lenlist(Node * head)//返回链表长度
int len;
Node * temp = head;
for(len=0; temp!=NULL; len++) temp=temp->next;
return len;
插入元素
void insertlist(Node ** list,int data,int index)//插入元素,用*list将head指针和next统一表示
if(index<0 || index>lenlist(*list))return;//判断非法输入
Node * newnode=(Node *)malloc(sizeof(Node));//创建
newnode->data=data;
newnode->next=NULL;
while(index--)list=&((*list)->next);//插入
newnode->next=*list;
*list=newnode;
尾部增加元素
void pushback(Node ** head,int data)//尾插,同上
Node * newnode=(Node *)malloc(sizeof(Node));//创建
newnode->data=data;
newnode->next=NULL;
while(*head!=NULL)head=&((*head)->next);//插入
*head=newnode;
清空链表
void freelist(Node ** head)//清空链表
Node * temp=*head;
Node * ttemp;
*head=NULL;//指针设为空
while(temp!=NULL)//释放
ttemp=temp;
temp=temp->next;
free(ttemp);
删除
void deletelist(Node ** list,int data)//删除链表节点
Node * temp;//作用只是方便free
while((*list)->data!=data && (*list)->next!=NULL)list=&((*list)->next);
if((*list)->data==data)
temp=*list;
*list=(*list)->next;
free(temp);
查找
Node * findnode(Node ** list,int data)//查找,返回指向节点的指针,若无返回空
while((*list)->data!=data && (*list)!=NULL) list=&((*list)->next);
return *list;
改值
void change(Node ** list,int data,int temp)//改变
while((*list)->data!=data && (*list)->next!=NULL)list=&((*list)->next);
if((*list)->data==data)(*list)->data=temp;
最后测试
int main(void)//测试
Node * head=NULL;
Node ** gg=&head;
int i;
for(i=0;i<10;i++)pushback(gg,i);
printf("链表元素依次为: ");
printlist(head);
printf("长度为%d\\n",lenlist(head));
freelist(gg);
printf("释放后长度为%d\\n",lenlist(head));
for(i=0;i<10;i++)pushback(gg,i);
deletelist(gg,0);//头
deletelist(gg,9);//尾
deletelist(gg,5);
deletelist(gg,100);//不存在
printf("再次创建链表,删除节点后\\n");
printlist(head);
freelist(gg);
for(i=0;i<5;i++)pushback(gg,i);
insertlist(gg,5,0);//头
insertlist(gg,5,5);
insertlist(gg,5,7);//尾
insertlist(gg,5,10);//不存在
printlist(head);
printf("找到%d\\n把3变为100",*findnode(gg,5));
change(gg,3,100);
change(gg,11111,1);//不存在
printlist(head);
约瑟夫环-(数组、循环链表、数学)
约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。
约瑟夫环运作如下:
1、一群人围在一起坐成环状(如:N)
2、从某个编号开始报数(如:S)
3、数到某个数(如:M)的时候,此人出列,下一个人重新报数
4、一直循环,直到所有人出列 ,约瑟夫环结束
模拟过程,求出最后的人。
把数组看成一个环,从第s个元素开始按m-1间隔删除元素,重复过程,直到元素全部去掉。
void Josephus(int a[],int n,int m,int s)
int i,j;
int k=n;
for(i=0;i<n;i++)a[i]=i+1;//编号
i=(s+n-1)%n;
while(k)
for(j=1;j<m;j++)i=(i+1)%k;//依次报数,头尾相连
printf("%d\\n",a[i]);//出局
for(j=i+1;j<k;j++)a[j-1]=a[j];//删除本节点
k--;
//模拟结束,最后输出的就是留下的人
可以用带头单循环链表来求解:
也是一样的,只是实现不同,给出核心代码:
while(k)
for(j=1;j<m;j++)
pr=p;
p=p->link;
if(p==head)//头结点跳过
pr=p;
p=p->link;
k--;
//打印
pr->link=p->link;//删结点
free(p);
p=pr->link;//从下一个继续
双向循环链表也可以解,和单链表类似,只是不需要保持前趋指针。
数学可解:
效率最高
int check_last_del(int n,int m)
int i = 1;
int ret = 0;
for (i = 2; i<=n;i++)
ret = (ret + m) %i;
return ret+1;//因为ret是从0到n-1,最后别忘了加1。
线性表表示集合
集合我们高中都学过吧?
最重要的几个特点:元素不能重复、各个元素之间没有关系、没有顺序
集合内的元素可以是单元素或者是集合。
对集合的操作:交集并集差集等,还有对自身的加减等。
需要频繁的加减元素,所以顺序存储效率较低,但是我们还是说一下是怎么实现的:
用01向量表示集合,因为现实中任何一个有穷集合都能对应到一个0、1、2.....n这么一个序列中。所以可以对应过来,每位的01代表这个元素存在与否即可。
链接存储表示使用有序链表来实现,虽然集合是无序的,但是我们的链表可以是有序的。可以按升序排列。而链表理论上可以无限增加,所以链表可以表示无限集。
下面我们来实现一下:
我们定义一个节点:
typedef int ElemType;
typedef struct SetNode//节点定义
ElemType data;//数据
struct SetNode * link;
*LinkedSet//集合定义
然后要实现那些操作了,首先想插入吧:我们对于一个新元素,查找集合中是否存在,存在就不插入,不存在就插入到查找失败位置。
删除也简单,查找存在就删除。
我们说两个集合的操作:
求两个集合的并:
两个链表,都是升序。把他们去重合并即可。
其实和链表归并的merge过程是一样的,只是相等的时候插入一个,两个指针都向后走就行了。
我就再写一遍吧。
void UnionSet(LinkedSet & A,LinkedSet & B,LinkedSet & C)
SetNode *pa=A->link,*pb=B->link,*pc=C;
while(pa && pb)//都不为空
if(pa->data==pb->data)//相等,插一次,两边向后
pc->link=new SetNode;
pc->data=pa->data;
pa=pa->link;
pb=pb->link;
else if(pa->data<pb->data)//插小的,小的向后
pc->link=new SetNode;
pc->data=pa->data;
pa=pa->link;
else
pc->link=new SetNode;
pc->data=pb->data;
pb=pb->link;
pc=pc->link;//注意指针
if(pa)p=pa;//剩下的接上
else p=pb;//只执行一个
while(p)//依次复制
pc->link=new SetNode;
pc->data=p->data;
pc=pc->link;
p=p->link;
pc->link=NULL;
求两个集合的交,更简单,还是这三种情况,谁小谁向后,相等才插入。
void UnionSet(LinkedSet & A,LinkedSet & B,LinkedSet & C)
SetNode *pa=A->link,*pb=B->link,*pc=C;
while(pa && pb)//都不为空
if(pa->data==pb->data)//相等,插一次,两边向后
pc->link=new SetNode;
pc->data=pa->data;
pa=pa->link;
pb=pb->link;
pc=pc->link;//注意指针,就不是每次都向后了,只有插入才向后
else if(pa->data<pb->data)//小的向后
pa=pa->link;
else
pb=pb->link;
pc->link=NULL;
求两个集合的差:高中可能没学这个概念,其实就是A-B,就是B中的元素,A都不能有了。
运算你可以把B元素全过一遍,A中有就去掉,但是这样时间复杂度太高了,我们需要O(A+B)而不是O(A*B)
因为有序,很好操作,还是两个指针,
如果AB相同,都向后移。
或者,B小,B就向后移。
如果A小,说明B中不含这个元素,我们把它复制到结果链表里。
思想还行,实在懒得写了,有时间再说吧。
线性表实现一元多项式操作
数组存放:
不需要记录幂,下标就是。
比如1,2,3,5表示1+2x+3x^2+5x^3
有了思路,我们很容易定义结构
typedef struct node
float * coef;//系数数组
int maxSize;//最大容量
int order;//最高阶数
Polynomial;
先实现求和:我们想求两个式子a+b,结果存在c中。
逻辑很简单,就是相加啊。
void Add(Polynomial & A,Polynomial & B,Polynomial & C)
int i;
int m=A.order;
int n=B.order;
for(i=0;i<=m && i<=n;i++)//共有部分加一起
C.coef[i]=A.coef[i]+B.coef[i];
while(i<=m)//只会执行一个,作用是把剩下的放入c
C.coef[i]=A.coef[i];
while(i<=n)
C.coef[i]=B.coef[i];
C.order=(m>n)?m:n;//等于较大项
实现乘法:
我们思考一下,两个多项式怎么相乘?
把a中每一项都和b中每一项乘一遍就好了。
高中知识
void Mul(Polynomial & A,Polynomial & B,Polynomial & C)
int i;
int m=A.order;
int n=B.order;
if(m+n>C.maxSize)
printf("超限");
return;
for(i=0;i<=m+n;i++)//注意范围,是最高项的幂加起来
以上是关于超硬核十万字!全网最全 数据结构 代码,随便秒杀老师/面试官,我说的的主要内容,如果未能解决你的问题,请参考以下文章
❤️爆肝新一代大数据存储宠儿,梳理了2万字 “超硬核” 文章!❤️