超硬核十万字!全网最全 数据结构 代码

本文代码实现基本按照《数据结构》课本目录顺序,外加大量的复杂算法实现,一篇文章足够。能换你一个收藏了吧?

当然如果落下什么了欢迎大家评论指出

目录

顺序存储线性表实现

单链表不带头标准c语言实现

单链表不带头压缩c语言实现

约瑟夫环-(数组、循环链表、数学)

线性表表示集合

线性表实现一元多项式操作

链表环问题

移除链表元素

回文链表

链表表示整数,相加

LRU

LFU

合并链表

反转链表

反转链表2

对链表排序

旋转链表

数组实现栈

链表实现栈

数组实现队列

链表实现队列

双栈的实现

栈/队列 互相模拟实现

栈的排序

栈——括号匹配

栈——表达式求值

借汉诺塔理解栈与递归

单调栈

双端单调队列

单调队列优化的背包问题

01背包问题

完全背包问题

多重背包问题

串的定长表示

串的堆分配实现

KMP

一、引子

二、分析总结

三、基本操作

四、原理

五、复杂度分析

Manacher

小问题一:请问,子串和子序列一样么?请思考一下再往下看

小问题二:长度为n的字符串有多少个子串?多少个子序列?

一、分析枚举的效率

二、初步优化

问题三:怎么用对称轴向两边扩的方法找到偶回文?(容易操作的)

那么请问,加进去的符号,有什么要求么?是不是必须在原字符中没出现过?请思考

小结:

三、Manacher原理

假设遍历到位置i,如何操作呢

四、代码及复杂度分析

前缀树

后缀树/后缀数组

后缀树:后缀树,就是把一串字符的所有后缀保存并且压缩的字典树。

相对于字典树来说,后缀树并不是针对大量字符串的,而是针对一个或几个字符串来解决问题。比如字符串的回文子串,两个字符串的最长公共子串等等。

后缀数组:就是把某个字符串的所有后缀按照字典序排序后的数组。(数组中保存起始位置就好了,结束位置一定是最后)

AC自动机

数组缺失

二叉树遍历

前序

中序

后序

进一步思考

二叉树序列化/反序列化

先序中序后序两两结合重建二叉树

先序遍历

中序遍历

后序遍历

层次遍历

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

输入某二叉树的后序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字

输入某二叉树的后序遍历和先序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字

先序中序数组推后序数组

二叉树遍历

遍历命名

方法1:我们可以重建整棵树:

https://blog.youkuaiyun.com/hebtu666/article/details/84322113

方法2:我们可以不用重建,直接得出:

根据数组建立平衡二叉搜索树

java整体打印二叉树

判断平衡二叉树

判断完全二叉树

判断二叉搜索树

二叉搜索树实现

堆的简单实现

堆应用例题三连

一个数据流中,随时可以取得中位数。

金条

项目最大收益(贪心问题)

并查集实现

并查集入门三连:HDU1213 POJ1611 POJ2236

HDU1213

POJ1611

POJ2236

线段树简单实现

功能:一样的,依旧是查询和改值。

查询[s,t]之间最小的数。修改某个值。

那我们继续说,如何查询。

如何更新?

树状数组实现

最大搜索子树

morris遍历

最小生成树

拓扑排序

最短路

简单迷宫问题

深搜DFS\广搜BFS

皇后问题

一般思路:

优化1:

优化2:

二叉搜索树实现

Abstract Self-Balancing Binary Search Tree

二叉搜索树

概念引入

AVL树

红黑树

size balance tree

伸展树

Treap

最简单的旋转

带子树旋转

代码实现

AVL Tree

前言

二叉搜索树

AVL Tree

旋转

旋转总结

单向右旋平衡处理LL:

单向左旋平衡处理RR:

双向旋转(先左后右)平衡处理LR:

双向旋转(先右后左)平衡处理RL:

深度的记录

单个节点的深度更新

写出旋转代码

总写调整方法

插入完工

删除

直观表现程序

跳表介绍和实现

c语言实现排序和查找所有算法


顺序存储线性表实现

在计算机中用一组地址连续的存储单元依次存储线性表的各个数据元素,称作线性表的顺序存储结构。

顺序存储结构的主要优点是节省存储空间,因为分配给数据的存储单元全用存放结点的数据(不考虑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+3x2+5x3

有了思路,我们很容易定义结构

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++)//注意范围,是最高项的幂加起来
        C.coef[i]=0.0;
    for(i=0;i<=m;i++)
    {
        for(j=0;j<=n;j++)
        {
            C.coef[i+j]+=A.coef[i]*B.coef[j];
        }
    }
    C.order=m+n;//注意范围,是最高项的幂加起来
}

利用数组存放虽然简单,但是当幂相差很大时,会造成空间上的严重浪费(包括时间也是),所以我们考虑采用链表存储。

我们思考一下如何存储和做运算。

我们肯定要再用一个变量记录幂了。每个节点记录系数和指数。

考虑如何相加:

对于c,其实刚开始是空的,我们首先要实现一个插入功能,然后,遍历a和b,进一步利用插入函数来不断尾插。

因为a和b都是升幂排列,所以相加的时候,绝对不会发生结果幂小而后遇到的情况,所以放心的一直插入就好了。

具体实现也比较好想:a和b幂相等就加起来,不等就小的单独插入,然后指针向后移。

加法就放老师写的代码吧,很漂亮的代码:(没和老师商量,希望不会被打)

老师原地插的,都一样都一样

老师原文:http://www.edu2act.net/article/shu-ju-jie-gou-xian-xing-biao-de-jing-dian-ying-yong/

void AddPolyn(polynomial &Pa, polynomial &Pb)
	//多项式的加法:Pa = Pa + Pb,利用两个多项式的结点构成“和多项式”。 
{
	LinkList ha = Pa;		//ha和hb分别指向Pa和Pb的头指针
	LinkList hb = Pb;
	LinkList qa = Pa->next;
	LinkList qb = Pb->next;	//ha和hb分别指向pa和pb的前驱
	while (qa && qb)		//如果qa和qb均非空
	{
		float sum = 0.0;
		term a = qa->data;
		term b = qb->data;
		switch (cmp(a,b))
		{
		case -1:	//多项式PA中当前结点的指数值小
			ha = qa;
			qa = qa->next;
			break;
		case 0:		//两者指数值相等
			sum = a.coef + b.coef;
			if(sum != 0.0)
			{	//修改多项式PA中当前结点的系数值
				qa->data.coef = sum;
				ha = qa;
			}else
			{	//删除多项式PA中当前结点
				DelFirst(ha, qa);
				free(qa);
			}
			DelFirst(hb, qb);
			free(qb);
			qb = hb->next;
			qa = ha->next;
			break;
		case 1:
			DelFirst(hb, qb);
			InsFirst(ha, qb);
			qb = hb->next;
			ha = ha->next;
			break;
		}//switch
	}//while
	if(!ListEmpty(Pb))
		Append(Pa,qb);
	DestroyList(hb);

}//AddPolyn

对于乘法,我们就不能一直往后插了,因为遍历两个式子,可能出现幂变小的情况。所以我们要实现一个插入函数,如果c中有这一项,就加起来,没这一项就插入。

我们先实现插入函数:(哦,对了,我没有像老师那样把系数和指数再定义一个结构体,都放一起了。还有next我写的link,还有点别的不一样,都无伤大雅,绝对能看懂)

void Insert(Polynomial &L,float c,int e)//系数c,指数e
{
    Term * pre=L;
    Term * p=L->link;
    while(p && p->exp<e)//查找
    {
        pre=p;
        p=p->link;
    }
    if(p->exp==e)//如果有这一项
    {
        if(p->coef+c)//如果相加是0了,就删除节点
        {
            pre->link=p->link;
            free(p);
        }
        else//相加不是0,就合并
        {
            p->coef+=c;
        }
    }
    else//如果没这一项,插入就好了,链表插入写了很多遍了
    {
            Term * pc=new Term;//创建
            pc->exp=e;
            pc->coef=c;
            pre->link=pc;
            pc->link=p;        
    }
}

插入写完了,乘法就好实现了,还是两个循环,遍历a和b,只是最后调用Insert方法实现就ok

insert(c,乘系数,加幂)

拓展:一维数组可以模拟一元多项式。类似的,二维数组可以模拟二元多项式。实现以后有时间写了再放链接。

链表环问题

1.判断单链表是否有环

使用两个slow, fast指针从头开始扫描链表。指针slow 每次走1步,指针fast每次走2步。如果存在环,则指针slow、fast会相遇;如果不存在环,指针fast遇到NULL退出。

就是所谓的追击相遇问题:

2.求有环单链表的环长

在环上相遇后,记录第一次相遇点为Pos,之后指针slow继续每次走1步,fast每次走2步。在下次相遇的时候fast比slow正好又多走了一圈,也就是多走的距离等于环长。

设从第一次相遇到第二次相遇,设slow走了len步,则fast走了2*len步,相遇时多走了一圈:

环长=2*len-len。

3.求有环单链表的环连接点位置

第一次碰撞点Pos到连接点Join的距离=头指针到连接点Join的距离,因此,分别从第一次碰撞点Pos、头指针head开始走,相遇的那个点就是连接点。

在环上相遇后,记录第一次相遇点为Pos,连接点为Join,假设头结点到连接点的长度为LenA,连接点到第一次相遇点的长度为x,环长为R

第一次相遇时,slow走的长度 S = LenA + x;

第一次相遇时,fast走的长度 2S = LenA + n*R + x;

所以可以知道,LenA + x = n*R;  LenA = n*R -x;

4.求有环单链表的链表长

上述2中求出了环的长度;3中求出了连接点的位置,就可以求出头结点到连接点的长度。两者相加就是链表的长度。

编程实现:

下面是代码中的例子:

具体代码如下:

#include <stdio.h>
#include <stdlib.h>
typedef struct node{
    int value;
    struct node *next;
}LinkNode,*Linklist;

/// 创建链表(链表长度,环节点起始位置)
Linklist createList(){
    Linklist head = NULL;
    LinkNode *preNode = head;
    LinkNode *FifthNode = NULL;
    for(int i=0;i<6;i++){
        LinkNode *tt = (LinkNode*)malloc(sizeof(LinkNode));
        tt->value = i;
        tt->next = NULL;
        if(preNode == NULL){
            head = tt;
            preNode = head;
        }
        else{
            preNode->next =tt;
            preNode = tt;
        }

        if(i == 3)
            FifthNode = tt;
    }
    preNode->next = FifthNode;
    return head;
}

///判断链表是否有环
LinkNode* judgeRing(Linklist list){
    LinkNode *fast = list;
    LinkNode *slow = list;

    if(list == NULL)
        return NULL;

    while(true){
        if(slow->next != NULL && fast->next != NULL && fast->next->next != NULL){
            slow = slow->next;
            fast = fast->next->next;
        }
        else
            return NULL;

        if(fast == slow)
            return fast;
    }
}

///获取链表环长
int getRingLength(LinkNode *ringMeetNode){
    int RingLength=0;
    LinkNode *fast = ringMeetNode;
    LinkNode *slow = ringMeetNode;
    for(;;){
        fast = fast->next->next;
        slow = slow->next;
        RingLength++;
        if(fast == slow)
            break;
    }
    return RingLength;
}

///获取链表头到环连接点的长度
int getLenA(Linklist list,LinkNode *ringMeetNode){
    int lenA=0;
    LinkNode *fast = list;
    LinkNode *slow = ringMeetNode;
    for(;;){
        fast = fast->next;
        slow = slow->next;
        lenA++;
        if(fast == slow)
            break;
    }
    return lenA;
}

///环起始点
///如果有环, 释放空空间时需要注意.
LinkNode* RingStart(Linklist list, int lenA){
    if (!list || lenA <= 0){
        return NULL;
    }

    int i = 0;
    LinkNode* tmp = list;
    for ( ; i < lenA; ++i){
        if (tmp != NULL){
            tmp = tmp->next;
        }
    }

    return (i == lenA)? tmp : NULL;
}

///释放空间
int freeMalloc(Linklist list, LinkNode* ringstart){
    bool is_ringstart_free = false; //环起始点只能被释放空间一次
    LinkNode *nextnode = NULL;

    while(list != NULL){
        nextnode = list->next;
        if (list == ringstart){ //如果是环起始点
            if (is_ringstart_free)
                break;  //如果第二次遇到环起始点addr, 表示已经释放完成
            else
                is_ringstart_free = true;   //记录已经释放一次
        }
        free(list);
        list = nextnode;
    }

    return 0;
}

int main(){
    Linklist list = NULL;
    LinkNode *ringMeetNode  = NULL;
    LinkNode *ringStartNode = NULL;

    int LenA       = 0;
    int RingLength = 0;

    list = createList();
    ringMeetNode = judgeRing(list); //快慢指针相遇点

    if(ringMeetNode == NULL)
        printf("No Ring\n");
    else{
        printf("Have Ring\n");
        RingLength = getRingLength(ringMeetNode);   //环长
        LenA = getLenA(list,ringMeetNode);

        printf("RingLength:%d\n", RingLength);
        printf("LenA:%d\n", LenA);
        printf("listLength=%d\n", RingLength+LenA);
    }

    ringStartNode = RingStart(list, LenA);  //获取环起始点
    freeMalloc(list, ringStartNode);    //释放环节点, 有环时需要注意. 采纳5楼建议
    return 0;
}

移除链表元素

删除链表中等于给定值 val 的所有节点。

示例:

输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5

思路:就删呗,注意第一个数可能会被删

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
	public ListNode removeElements(ListNode head, int val) {
		ListNode p = new ListNode(-1);
		p.next = head;
		//因为要删除的可能是链表的第一个元素,所以用一个h节点来做处理
		ListNode h = p;
		while(p.next!=null) {
			if(p.next.val==val) {
				p.next = p.next.next;
			}else{
                p = p.next;
            }	
		}
		return h.next;
	}
}

回文链表

请判断一个链表是否为回文链表。

示例 1:

输入: 1->2
输出: false
示例 2:

输入: 1->2->2->1
输出: true
进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

思路:逆置前一半,然后从中心出发开始比较即可。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isPalindrome(ListNode head) {
        if(head == null || head.next == null) {
            return true;
        }
        ListNode slow = head, fast = head;
        ListNode pre = head, prepre = null;
        while(fast != null && fast.next != null) {
            pre = slow;
            slow = slow.next;
            fast = fast.next.next;
            pre.next = prepre;
            prepre = pre;
        }
        if(fast != null) {
            slow = slow.next;
        }
        while(pre != null && slow != null) {
            if(pre.val != slow.val) {
                return false;
            }
            pre = pre.next;
            slow = slow.next;
        }
        return true;
    }
}

链表表示整数,相加

思路:就模仿加法即可。。。题目还贴心的给把顺序反过来了。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode ans=new ListNode(0);
        ListNode tempA=l1;
        ListNode tempB=l2;
        ListNode temp=ans;
        int out=0;
        while(tempA!=null || tempB!=null){
            int a=tempA!=null?tempA.val:0;
            int b=tempB!=null?tempB.val:0;
            ans.next=new ListNode((a+b+out)%10);
            ans=ans.next;
            out=(a+b+out)/10;
            if(tempA!=null)tempA=tempA.next;
            if(tempB!=null)tempB=tempB.next;
        }
        if(out!=0){
          ans.next=new ListNode(out);  
        }
        return temp.next;
    }
}

LRU

LRU全称是Least Recently Used,即最近最久未使用的意思。

LRU算法的设计原则是:如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。也就是说,当限定的空间已存满数据时,应当把最久没有被访问到的数据淘汰。(这一段是找的,让大家理解一下什么是LRU)。

说一下我们什么时候见到过LRU:其实老师们肯定都给大家举过这么个例子:你在图书馆,你把书架子里的书拿到桌子上。。但是桌子是有限的,你有时候不得不把一些书放回去。这就相当于内存和硬盘。这个例子都说过吧?

LRU就是记录你最长时间没看过的书,就把它放回去。在cache那里见过吧

然后最近在研究redis,又看到了这个LRU,所以就想写一下吧。

题目:设计一个结构,这个结构可以查询K-V,但是容量有限,当存不下的时候就要把用的年代最久远的那个东西扔掉。

其实思路很简单,我们维护一个双向链表即可,get也就是使用了,我们就把把它提到最安全的位置。新来的KV就依次放即可。

我们就先写这个双向链表结构

先写节点结构:

	public static class Node<V> {
		public V value;
		public Node<V> last;//前
		public Node<V> next;//后

		public Node(V value) {
			this.value = value;
		}
	}

然后写双向链表结构: 我们没必要把链表操作都写了,分析一下,我们只有三个操作:

1、加节点

2、使用了某个节点就把它调到尾,代表优先级最高

3、把优先级最低的移除,也就是去头部

(不会的,翻我之前的链表操作都有写)

	public static class NodeDoubleLinkedList<V> {
		private Node<V> head;//头
		private Node<V> tail;//尾

		public NodeDoubleLinkedList() {
			this.head = null;
			this.tail = null;
		}

		public void addNode(Node<V> newNode) {
			if (newNode == null) {
				return;
			}
			if (this.head == null) {//头空
				this.head = newNode;
				this.tail = newNode;
			} else {//头不空
				this.tail.next = newNode;
				newNode.last = this.tail;//注意让本节点前指针指向旧尾
				this.tail = newNode;//指向新尾
			}
		}
/*某个点移到最后*/
		public void moveNodeToTail(Node<V> node) {
			if (this.tail == node) {//是尾
				return;
			}
			if (this.head == node) {//是头
				this.head = node.next;
				this.head.last = null;
			} else {//中间
				node.last.next = node.next;
				node.next.last = node.last;
			}
			node.last = this.tail;
			node.next = null;
			this.tail.next = node;
			this.tail = node;
		}
/*删除第一个*/
		public Node<V> removeHead() {
			if (this.head == null) {
				return null;
			}
			Node<V> res = this.head;
			if (this.head == this.tail) {//就一个
				this.head = null;
				this.tail = null;
			} else {
				this.head = res.next;
				res.next = null;
				this.head.last = null;
			}
			return res;
		}

	}

链表操作封装完了就要实现这个结构了。

具体思路代码注释

	public static class MyCache<K, V> {
		//为了kv or vk都能查
		private HashMap<K, Node<V>> keyNodeMap;
		private HashMap<Node<V>, K> nodeKeyMap;
		//用来做优先级
		private NodeDoubleLinkedList<V> nodeList;
		private int capacity;//容量

		public MyCache(int capacity) {
			if (capacity < 1) {//你容量连1都不给,捣乱呢
				throw new RuntimeException("should be more than 0.");
			}
			this.keyNodeMap = new HashMap<K, Node<V>>();
			this.nodeKeyMap = new HashMap<Node<V>, K>();
			this.nodeList = new NodeDoubleLinkedList<V>();
			this.capacity = capacity;
		}

		public V get(K key) {
			if (this.keyNodeMap.containsKey(key)) {
				Node<V> res = this.keyNodeMap.get(key);
				this.nodeList.moveNodeToTail(res);//使用过了就放到尾部
				return res.value;
			}
			return null;
		}

		public void set(K key, V value) {
			if (this.keyNodeMap.containsKey(key)) {
				Node<V> node = this.keyNodeMap.get(key);
				node.value = value;//放新v
				this.nodeList.moveNodeToTail(node);//我们认为放入旧key也是使用过
			} else {
				Node<V> newNode = new Node<V>(value);
				this.keyNodeMap.put(key, newNode);
				this.nodeKeyMap.put(newNode, key);
				this.nodeList.addNode(newNode);//加进去
				if (this.keyNodeMap.size() == this.capacity + 1) {
					this.removeMostUnusedCache();//放不下就去掉优先级最低的
				}
			}
		}

		private void removeMostUnusedCache() {
			//删除头
			Node<V> removeNode = this.nodeList.removeHead();
			K removeKey = this.nodeKeyMap.get(removeNode);
			//删除掉两个map中的记录
			this.nodeKeyMap.remove(removeNode);
			this.keyNodeMap.remove(removeKey);
		}
	}

LFU

请你为 最不经常使用(LFU)缓存算法设计并实现数据结构。可以自行百度介绍,非常著名的结构

实现 LFUCache 类:

LFUCache(int capacity) - 用数据结构的容量 capacity 初始化对象
int get(int key) - 如果键存在于缓存中,则获取键的值,否则返回 -1。
void put(int key, int value) - 如果键已存在,则变更其值;如果键不存在,请插入键值对。当缓存达到其容量时,则应该在插入新项之前,使最不经常使用的项无效。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除 最久未使用 的键。
注意「项的使用次数」就是自插入该项以来对其调用 get 和 put 函数的次数之和。使用次数会在对应项被移除后置为 0 。

为了确定最不常使用的键,可以为缓存中的每个键维护一个 使用计数器 。使用计数最小的键是最久未使用的键。

当一个键首次插入到缓存中时,它的使用计数器被设置为 1 (由于 put 操作)。对缓存中的键执行 get 或 put 操作,使用计数器的值将会递增。

你可以为这两种操作设计时间复杂度为 O(1) 的实现吗?

// 缓存的节点信息
struct Node {
    int key, val, freq;
    Node(int _key,int _val,int _freq): key(_key), val(_val), freq(_freq){}
};
class LFUCache {
    int minfreq, capacity;
    unordered_map<int, list<Node>::iterator> key_table;
    unordered_map<int, list<Node>> freq_table;
public:
    LFUCache(int _capacity) {
        minfreq = 0;
        capacity = _capacity;
        key_table.clear();
        freq_table.clear();
    }
    
    int get(int key) {
        if (capacity == 0) return -1;
        auto it = key_table.find(key);
        if (it == key_table.end()) return -1;
        list<Node>::iterator node = it -> second;
        int val = node -> val, freq = node -> freq;
        freq_table[freq].erase(node);
        // 如果当前链表为空,我们需要在哈希表中删除,且更新minFreq
        if (freq_table[freq].size() == 0) {
            freq_table.erase(freq);
            if (minfreq == freq) minfreq += 1;
        }
        // 插入到 freq + 1 中
        freq_table[freq + 1].push_front(Node(key, val, freq + 1));
        key_table[key] = freq_table[freq + 1].begin();
        return val;
    }
    
    void put(int key, int value) {
        if (capacity == 0) return;
        auto it = key_table.find(key);
        if (it == key_table.end()) {
            // 缓存已满,需要进行删除操作
            if (key_table.size() == capacity) {
                // 通过 minFreq 拿到 freq_table[minFreq] 链表的末尾节点
                auto it2 = freq_table[minfreq].back();
                key_table.erase(it2.key);
                freq_table[minfreq].pop_back();
                if (freq_table[minfreq].size() == 0) {
                    freq_table.erase(minfreq);
                }
            } 
            freq_table[1].push_front(Node(key, value, 1));
            key_table[key] = freq_table[1].begin();
            minfreq = 1;
        } else {
            // 与 get 操作基本一致,除了需要更新缓存的值
            list<Node>::iterator node = it -> second;
            int freq = node -> freq;
            freq_table[freq].erase(node);
            if (freq_table[freq].size() == 0) {
                freq_table.erase(freq);
                if (minfreq == freq) minfreq += 1;
            }
            freq_table[freq + 1].push_front(Node(key, value, freq + 1));
            key_table[key] = freq_table[freq + 1].begin();
        }
    }
};

合并链表

将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

思路:链表归并。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode head=new ListNode(0);
        ListNode temp=head;
        while(l1!=null && l2!=null){
            if(l1.val>l2.val){
                temp.next=l2;
                l2=l2.next;
            }else{
                temp.next=l1;
                l1=l1.next;  
            }
            temp=temp.next;
        }
        if(l1!=null){
            temp.next=l1;
        }else{
            temp.next=l2;
        }
        return head.next;
    }
}

反转链表

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

经典题不解释,画图自己模拟记得清楚

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode prev = null;
        ListNode curr = head;
        while (curr != null) {
            ListNode nextTemp = curr.next;
            curr.next = prev;
            prev = curr;
            curr = nextTemp;
        }
        return prev;
    }
}

反转链表2

反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。

说明:
1 ≤ m ≤ n ≤ 链表长度。

示例:

输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL

思路:反转链表,只不过是反转一部分,注意这一部分逆序之前做好记录,方便逆序完后可以链接上链表的其他部分。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseBetween(ListNode head, int m, int n) {
        if (head == null) return null;
        ListNode cur = head, prev = null;
        while (m > 1) {
            prev = cur;
            cur = cur.next;
            m--;
            n--;
        }
        ListNode con = prev, tail = cur;
        ListNode third = null;
        while (n > 0) {
            third = cur.next;
            cur.next = prev;
            prev = cur;
            cur = third;
            n--;
        }
        if (con != null) {
            con.next = prev;
        } else {
            head = prev;
        }
        tail.next = cur;
        return head;
    }
}

对链表排序

丢人,我就是按插入排序老老实实写的啊。。。。

别人肯定map了hhh。

对链表进行插入排序。

插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。
每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。

插入排序算法:

插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
重复直到所有输入数据插入完为止。

示例 1:

输入: 4->2->1->3
输出: 1->2->3->4
示例 2:

输入: -1->5->3->4->0
输出: -1->0->3->4->5

思路:按插入排序思路写就可以啦,只是注意链表操作,比数组麻烦很多。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode insertionSortList(ListNode head) {
        ListNode ans=new ListNode(-1);
        ListNode temp=null;//要插入的地方
        ListNode key=null;//要插入的值
        while(head!=null){
            key=head;
            temp=ans;
            while(temp.next!=null && key.val>temp.next.val){
                temp=temp.next;
            }
            head=head.next;
            key.next=temp.next;
            temp.next=key;
        }
        return ans.next;

    }
}

旋转链表

给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。

示例 1:

输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
向右旋转 1 步: 5->1->2->3->4->NULL
向右旋转 2 步: 4->5->1->2->3->NULL
示例 2:

输入: 0->1->2->NULL, k = 4
输出: 2->0->1->NULL
解释:
向右旋转 1 步: 2->0->1->NULL
向右旋转 2 步: 1->2->0->NULL
向右旋转 3 步: 0->1->2->NULL
向右旋转 4 步: 2->0->1->NULL

思路:找准断点,直接调指针即可。

注意:长度可能超过链表长度,要取模。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode rotateRight(ListNode head, int k) {
        if(head==null){
            return null;
        }
        int len=0;
        ListNode temp=head;
        while(temp!=null){
            temp=temp.next;
            len++;
        }
        k=k%len;
        ListNode node=head;
        ListNode fast=head;
        while(k-->0){
            fast=fast.next;
        }
        while(fast.next!=null){
            node=node.next;
            fast=fast.next;
        }
        fast.next=head;
        ListNode ans=node.next;
        node.next=null;
        return ans;

    }
}

数组实现栈

学习了改进,利用define typedef比上次写的链表更容易改变功能,方便维护,代码更健壮。

大佬别嫌弃,萌新总是很笨,用typedef都想不到。

#include<stdio.h>
#include<stdbool.h>
#define maxsize 10
typedef int datatype;
typedef struct stack
{
    datatype data[maxsize];
    int top;
}Stack;
Stack s;
void init()//初始化
{
    s.top=-1;
}
int Empty()//是否空
{
    if(s.top==-1)return 1;
    return 0;
}
int full()//是否满
{
    if(s.top==maxsize-1)return 1;
    return 0;
}
void Push(datatype element)//入栈
{
    if(!full()){
        s.top++;
        s.data[s.top]=element;
    }
    else printf("栈满\n");
}
void Pop()//出栈
{
    if(!Empty()) s.top--;
    else printf("栈空\n");
}
datatype Top()//取栈顶元素
{
    if(!Empty()) return s.data[s.top];
    printf("栈空\n");
}
void Destroy()//销毁
{
    s.top=-1;
}

测试不写了。

链表实现栈

栈,是操作受限的线性表,只能在一端进行插入删除。

其实就是带尾指针的链表,尾插

#include <stdio.h>
#include <stdlib.h>
#define OK 1
#define ERROR 0
#define Status int
#define SElemType int
//只在头部进行插入和删除(不带头结点)
typedef struct LNode
{
	SElemType data;
	struct LNode *next;
}LNode, *LinkList;

typedef struct 
{
	LNode *top;
	LNode *base;
	int length;
}LinkStack;


Status InitStack(LinkStack &S)
{
	S.base = NULL;
	S.top = NULL;
	S.length = 0;
	return OK;
}

Status GetTop(LinkStack S, SElemType &e)
{
	if(S.length == 0)
		return ERROR;
	e = S.top->data ;
	return OK;
}

Status Push(LinkStack &S, SElemType e)
{
	LNode *newNode = (LNode *)malloc(sizeof(LNode));
	newNode->data = e;
	newNode->next = S.top;
	S.top = newNode;
	if(!S.base)
		S.base = newNode;
	++S.length;
	return OK;
}

Status Pop(LinkStack &S, SElemType &e)
{
	LNode *p = S.top;
	if(S.length == 0)
		return ERROR;
	e = S.top->data;
	S.top = S.top->next;
	free(p);
	--S.length;
	return OK;
}

void PrintStack(LinkStack S)
{
	LNode *p = S.top;
	printf("由栈顶到栈底:");
	while (p)
	{
		printf("%d  ",p->data);
		p = p->next;
	}
	printf("\n");
}


int main(void)
{
	LinkStack LS;
	InitStack(LS);
	Push(LS,11);
	Push(LS,22);
	Push(LS,33);
	Push(LS,44);
	Push(LS,55);
	PrintStack(LS);
	SElemType e ;
	GetTop(LS , e);
	printf("栈顶元素是: %d\n",e);
	Pop(LS,e);
	PrintStack(LS);
	Pop(LS,e);
	PrintStack(LS);



	return 0;
}

数组实现队列

数组实现队列结构:

相对栈结构要难搞一些,队列的先进先出的,需要一个数组和三个变量,size记录已经进来了多少个元素,不需要其它萌新看不懂的知识。

触底反弹,头尾追逐的感觉。

循环使用数组。

具体解释一下触底反弹:当我们的队头已经到了数组的底,我们就把对头设为数组的第一个元素,对于队尾也是一样。实现了对数组的循环使用。

#include<stdio.h>
#include<stdbool.h>
#define maxsize 10
typedef int datatype;
typedef struct queue
{
    datatype arr[maxsize];
    int a,b,size;//头、尾、数量
}queue;
queue s;
void init()//初始化
{
    s.a=0;
    s.b=0;
    s.size=0;
}
int Empty()//判空
{
    if(s.size==0)return 1;
    return 0;
}
int full()//判满
{
    if(s.size==maxsize)return 1;
    return 0;
}
datatype peek()//查看队头
{
    if(s.size!=0)return s.arr[s.a];
    printf("queue is null\n");
}
datatype poll()//弹出队头
{
    int temp=s.a;
    if(s.size!=0)
    {
        s.size--;
        s.a=s.a==maxsize-1? 0 :s.a+1;//触底反弹
        return s.arr[temp];
    }
    printf("queue is null\n");
}
int push(datatype obj)//放入队尾
{
    if(s.size!=maxsize)
    {
        s.size++;
        s.arr[s.b]=obj;
        s.b=s.b==maxsize-1? 0 : s.b+1;//触底反弹
        return 1;
    }
    printf("queue is full\n");
    return 0;
}
//测试
int main()
{
    int i;
    init();
    if(Empty())pr
最合逻辑最大延迟计算是指在给定的逻辑约束下,计算任务的最大延迟时间。这个问题可以通过使用人工智能相关算法和遗传算法来求解。这些算法可以利用多线程技术来缩短计算时间。 对于符串匹配问题,KMP算法是一种时间复杂度为O(len(s) * len(t)) 的快速算法。通过遍历两个符串,可以有效地找到匹配的结果。在这个问题中,我认为KMP算法已经是时间复杂度更低的算法了。 在算法设计中,优化通常是通过减少重复运算和操作来实现的。当我们使用暴力递归时,我们应该分析出哪些重复运算,并对其进行优化。通过使用较少的空间来减少大量的时间,这是值得考虑的优化思路。 综上所述,最合逻辑最大延迟计算可以通过使用人工智能相关算法和遗传算法,并结合多线程技术来求解。对于符串匹配问题,KMP算法是一种时间复杂度较低且快速的算法。在算法设计中,我们可以通过优化重复运算来减少计算时间。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [硬核十万全网最全 数据结构 代码,随便秒杀老师/面试官,我说的](https://blog.youkuaiyun.com/hebtu666/article/details/115587600)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值