数据结构笔记1线性表及其实现

终于开始学习数据结构了 c语言结束之后 我们通过题目来巩固了 接下来我们来学习数据结构

这里我们将去认识到数据结构的一些基础知识,我在第一次学习的时候会很迷糊现在重新学习发现之前的时候还是因为c语言学的不牢固导致学习数据结构困难 这里 我会尽量的多写代码注释

目录

一什么是数据结构?

二线性表

2.1概述

2.2特点

2.3储存方式

2.4线性表的基本操作

2.5线性表的顺序存储结构

2.5.1概述

2.5.2 创建顺序线性表

2.5.3获得元素操作

2.5.4插入操作

2.5.6删除操作

2.6线性表的链式存储结构

2.6.1概述

2.6.2 单链表的建立

 2.6.3单链表的读取

2.6.4单链表的插入

 2.6.5单链表的删除

2.6.6链表的整表删除


一什么是数据结构?

1.1 概述:指互相之间存在着一种或多种特定关系的数据元素的集合,包括逻辑结构,存储结构和对数据的运算。(数据元素都不是孤立存在的)

二线性表

2.1概述

:0个或多个数据元素的有限序列 

2.2特点

               1 元素之间是有顺序的 

                2 若元素存在多个,则第一个元素无前驱,最后一个元素无后驱 其他每个元素有且只有一个前驱和后驱

                3 元素的个数是有限的

2.3储存方式

                       顺序储存

                      链式储存

2.4线性表的基本操作

InitList(&L):初始化表。构造一个空的线性表 L,并分配内存空间。

ListEmpty(L):若线性表为空,返回true 否则返回false

ClearList (*L):将线性表清空

GetElem(L,i,*e):将线性表L中的第i个元素值返回给e

LocateElem(L,e):在线性表L中查找与给定值e相等的元素,如果查找成功,返回该元素在表中序号表示成功否则返回0 表示失败

ListInsert(*L,i,e):在线性表L中第i个位置插入新元素e

ListDelete(*L,i,*e):删除线性表L中第i个位置元素,并用e返回其值

LiseLength(L):返回线性表L的元素个数

这里的基本操作 后面我们会都写出来的

2.5线性表的顺序存储结构

2.5.1概述

:用一段地址连续的储存单元依次储存线性表的数据元素

那说到这里的定义,大家是不是会联想到数组 数组的定义:数组由数据类型相同的一系列元素组成 即开辟一块内存空间 给数组 数组每相邻的元素不仅逻辑上相邻物理地址上也是相邻的

是不是和线性表的顺序储存结构一样 所以我们利用数组来完成线性表的顺序储存

我们来看线性表的顺序储存的结构代码

#define MAXSIZE 20 /*储存空间初始分配量 值无具体要求看线性表的大小*/
typedef int ElemType;/*这里我们将int类型取个别名为ElemType 
                      方便我们后面对线性表的类型进行定义 
                      ElemType也就是线性表的类型根据实际情况定,这里以int举例*/
typedef struct
{
    ElemType data[MAXSIZE]; //数组储存数据元素,最大值为MAXSIZE
    int length;
}Sqlist;//定义了一个结构体类型,别名为Sqlist 

这里我们发现描述顺序存储结构需要三个属性:

1 存储空间的起始位置:数组data,它的存储位置就是存储空间的存储位置

2 线性表的最大存储空间:数组的长度MAXSIZE

3 线性表的当前长度:length。

我们再来补充一点:数据长度和线性表长度的区别

数组的长度是存放线性表的储存空间的长度,存储分配后一般不变,除非是动态分配

线性表的长度是线性表中数据元素的个数 伴随着线性表的插入和删除操作的进行这个量会进行改变。

简单的说呢 线性表存放在数组里面 但不一定是达到最大值存放的 所以线性表的长度是小于或等于数组的长度

这里我们举个例子,比如数组data[5] 数组的长度是5 线性表只有俩个元素 只用到了俩个数组元素

所以线性表的长度是2

2.5.2 创建顺序线性表

我们来通过示例来创建一个线性表

#include <stdio.h>
#define MAXSIZE 5 //这里我们举例就不用那么大的了 最大为5
typedef struct 
{
    int data[MAXSIZE];
    int length;
}Sqlist; /*这里的注释上面有 我就不提了*/
/*初始化线性表函数 我们需要对线性表进行操作 所以将线性表的地址传入进来
  通过指针对线性表进行操作*/
void Initlist(Sqlist *L)
{
    L->length=5;//线性表的长度为5
    for(int i=0;i<5;i++)//将线性表的每个元素都赋值
    {
        L->data[i]=i+1;
    }
}

int main()
{
    Sqlist L;//建立一个线性表
    Initlist(&L);//初始化线性表
    for(int i;i<5;i++)//输出线性表的每个元素
    {
        printf("L.data[i]=%d\n",L.data[i]);
    }
    return 0;
}

这里需要特别注意,我们需要对线性表进行初始化,传入函数的参数一定不能错了 需要通过指针去操作的,这里我们的一个线性表就创建好了 我们将来一一介绍对线性表的操作

上面有过注释的 下面的代码 很有可能就不带了 大家需要一步一步的去理解 每一部分的注释我肯定都会写过的

2.5.3获得元素操作

概述:返回线性表L中的第i个元素的值

思路:就是将线性表的指定位置的值给输出出来

#include <stdio.h>
#define MAXSIZE 5 
typedef struct 
{
    int data[MAXSIZE];
    int length;
}Sqlist;
void Initlist(Sqlist *L)
{
    L->length=5;
    for(int i=0;i<5;i++)
    {
        L->data[i]=i+2;
    }
}
//获得指定位置元素的值,参数是L 和指定位置比如指定获得线性表第二个元素的值
int GetElem(Sqlist L,int i)
{
    if(L.length==0 || i<1||i>L.length)//防止意外情况 返回0 即错误
        return 0;
    else
    {
        return i-1;/*在查找操作正确的时候,返回指定值的数组下标 因为数组下标是从0开始的 
                     第2个元素的数组下标实际上是1所以返回指定位置-1即是数组的下标*/
    }
}
int main()
{
    Sqlist L;
    Initlist(&L);
    for(int i=0;i<5;i++)
     {
        printf("L.data[i]:%d\n",L.data[i]);
     }
    int a;
    a=GetElem(L,2);//用a来接收GetElem的值 并判断是否查找正常
    if(a)
    {
      printf("指定位置的值是%d\n",L.data[a]);       
    }
    else
     printf("获取指定位置的值出现错误 ");
    return 0;
}

2.5.4插入操作

概述:在线性表L中的第i个位置插入新元素e

思路:1 如果插入位置不合理,即错误

           2 如果线性表的长度等于数组的长度时即饱和了 无法进行插入 即错误除非动态增加容量

           3 从最后一个元素开始向前遍历到第i个位置,分别将它们都往后移动一个位置

           4 表长加1

代码有点长是因为我是举具体示例 实际上 这里只是ListInsert的应用

#include <stdio.h>
#define MAXSIZE 10 
typedef struct 
{
    int data[MAXSIZE];
    int length;
}Sqlist;
void Initlist(Sqlist *L)
{
    L->length=5;
    for(int i=0;i<5;i++)
    {
        L->data[i]=i+2;
    }
    for(int j=5;j<10;j++)
    {
        L->data[j]=0;
    }
}
int ListInsert(Sqlist *L,int i,int e)
{
    /*这里的判断条件是异常情况
      L->length==0 线性表长度为0
      L->length==MAXSIZE 线性表的长度等于数组的长度饱和了无法插入
      i>L->length+1 插入的位置在数组内,但是大于线性表的长度*/
    if(L->length==0 || L->length==MAXSIZE || i<1 || i>L->length+1)
    {
        return 0;
    }
    /*如果插入的位置不在表尾 我们需要将i后的每一个元素都往后移动一位 将i位置空出来给新元素*/
    if(i<=L->length)
    {
        //这里的判断循环条件大家注意一下 j>=i-1 因为我们需要将i位置在数组下标就是i-1的位置空出来
        //所以包括i-1的位置也需要往后移动 这里判断才有=号
        for(int j=L->length;j>=i-1;j--)
        {
            L->data[j+1]=L->data[j];//每个元素往后移动一位,并不会影响到未移动的元素
        }
    }
    L->data[i-1]=e;//i-1的数组位置已经空出来了直接插入就可以
    L->length++;//因为加入了新元素 表长加一
    return 1;//代表插入成功
}
int main()
{
    Sqlist L;
    Initlist(&L);
    for(int i=0;i<5;i++)
     {
        printf("L.data[i]:%d\n",L.data[i]);
     }
    int e=66;
     printf("插入后\n");
    if(ListInsert(&L,2,e))//如果插入成功,输出插入后的线性表的元素
    {
        for(int i=0;i<L.length;i++)
        {
         printf("L.data[i]:%d\n",L.data[i]);
        }
    }
    else
    {
        printf("插入错误\n");
    }
    return 0;
}

2.5.6删除操作

删除算法的思路:如果删除的位置不合理 报错

                          :取出删除元素

                          :从删除元素位置开始遍历到最后一个元素位置,分别将它们都向前移动一位

                          :表长减一

#include <stdio.h>
#define MAXSIZE 10 
typedef struct 
{
    int data[MAXSIZE];
    int length;
}Sqlist;
void Initlist(Sqlist *L)
{
    L->length=5;
    for(int i=0;i<5;i++)
    {
        L->data[i]=i+2;
    }
} 
//删除 函数的参数是线性表地址和删除的位置i
int Listdelete(Sqlist *L,int i)
{
    if(L->length==0 || i<1 || i>L->length+1)//异常情况 前面都有详细描述 
    {
        return 0;
    }

    if(i<L->length)//如果不是尾元素
    {
       for(int j=i-1;j<=L->length;j++)//将i之后的元素全部往前移动一位 
         {
            L->data[j]=L->data[j+1];
         }
    }
    L->length--;// 表长减一
    return 1;
}
int main()
{
    Sqlist L;
    Initlist(&L);
    for(int i=0;i<5;i++)
     {
        printf("L.data[i]:%d\n",L.data[i]);
     }
    printf("删除之后的线性表\n");
    if(Listdelete(&L,2))
    {
        for(int i=0;i<L.length;i++)
      {
        printf("L.data[i]:%d\n",L.data[i]);
      }
    }
    return 0;
}

说到这里我们来总结一下线性表的顺序存储结构的优缺点

优点:无须为表示表中元素之间的逻辑关系而增加额外的存储空间

        :可以快速地存取表中任一位置的元素

缺点:插入和删除操作需要移动大量元素

       :当线性表长度变化较大时,难以确定存储空间的容量

       :造成存储空间的碎片

2.6线性表的链式存储结构

2.6.1概述

:用一组任意的存储单元存储线性表的数据元素,这组存储单元可以是连续的也可以是不连续的。即这些数据元素可以存在内存未被占用的任意位置。

前面我们说线性表的特点的时候说过线性表应该是有序的 但这里为什么是任意的呢?

因为在链式结构中,除了要存数据元素信息外,还要存储它的后继元素的存储地址

因此:为了表示每个数据元素ai与其直接后继数据元素ai+1之间的逻辑关系,对数据元素ai来说,除了存储其本身的信息之外,还需要存储一个指示其直接后继的信息(即直接后继的存储位置)。我们把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域。指针域中存储的信息称做指针或链。这俩部分信息组成数据元素ai的存储映像,称为结点

其实简单的来说,链式存储有俩个部分组成,一个是数据域 一个是指针域 因为地址可以是任意的,所以通过指针来将线性表中的结点去连接起来。

这里我们介绍几个特殊的地方:对于线性表来说,有头有尾,链表也一样。我们把链表中第一个结点的存储位置叫做头指针,整个链表的存取必须是从头指针开始进行的,之后的每一个结点,其实是上一个的后继指针指向的位置 ,其中最后一个结点的指针指向空

我们有时会在单链表的第一个结点前,设一个结点,称为头结点,头结点的数据域不存储任何信息

头结点的指针域指向第一个结点的指针

我们来看一下头节点和头指针的异同

头指针:头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针

           :头指针具标识作用,所以常用头指针冠以链表的名字

          :无论链表是否为空,头指针均不为空,头指针是链表的必要元素

头结点:头结点是为了操作的统一和方便而设立的,放在第一元素的结点之前,其数据域一般无意义

          :有了头结点,对于第一元素结点前插入和删除第一结点,其操作与其他结点的操作就统一了

           :头结点不一定是链表的必须要素

2.6.2 单链表的建立

这里我们来看一下c语言的实现先来看结构体

typedef struct node{
	int data;//数据域
	struct node *next;//指针域
}node, *LinkList;  //结构体的定义

再来看一下建立单链表,需要注意我们新建一个新结点就需要新开辟一次内存空间出来

为什么需要建立三个指针 一个是头指针,一个是用于新建结点,一个是用于存储上一次建立结点

通过存储上一次建立的结点 将上一次的结点与新建立的结点进行链接

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
typedef struct node{
	int data;
	struct node *next;
}node, *LinkList;  //结构体的定义

LinkList create_Link()
{  //创建单链表的主要代码(尾插法建立单链表)
	LinkList head = NULL;//建立一个头指针,用于指向第一个结点
	LinkList temp = NULL;
	LinkList tail = NULL;
    head=(LinkList)malloc(sizeof(node));//声明头指针的内存
    head->next=NULL;//将头指针指向空
    int x;//用于键入结点的数据域 也是用来判断循环结束的条件
    scanf("%d",&x);
    while(x!=0)//当建立足够的结点的时候 键入0 停止建立
    {
       temp=(LinkList)malloc(sizeof(node));//声明一块内存是用来存储新结点
       temp->next=NULL;//将新结点的指针指向空防止野指针
       temp->data=x;//新结点的数据域数据就是键入的值
       if(head->next==NULL)//当第一次插入的时候把头指针指向第一个结点
       {
         head->next=temp;
       }
       else//非第一次 ,将上一次的结点的指针域指向新结点
       {
         tail->next=temp;
       }
       tail=temp;//用于保存上一次建立的结点,用于中间值
       scanf("%d",&x);
    }
    tail->next=NULL;//将最后一个结点的指针域指向空
    return head;
}

int main(){  //主函数
	LinkList L = create_Link();
    L=L->next;
    while(L!=NULL)
    {
        printf("%d\n",L->data);
        L=L->next;
    }
	return 0;
}

 这里我们重新新建一个线性表将数据域元素改成赋值 而不是键入 方便我们后面其他操作 比如删除插入等,核心代码是不会变得

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
typedef struct node{
	int data;
	struct node *next;
}node, *LinkList;  //结构体的定义

LinkList create_Link()
{  //创建单链表的主要代码(尾插法建立单链表)
	LinkList head = NULL;//建立一个头指针,用于指向第一个结点
	LinkList temp = NULL;
	LinkList tail = NULL;
    head=(LinkList)malloc(sizeof(node));//声明头指针的内存
    head->next=NULL;//将头指针指向空
    for(int i=0;i<5;i++)
    {
       temp=(LinkList)malloc(sizeof(node));//声明一块内存是用来存储新结点
       temp->next=NULL;//将新结点的指针指向空防止野指针
       temp->data=i;//新结点的数据域数据就是键入的值
       if(head->next==NULL)//当第一次插入的时候把头指针指向第一个结点
       {
         head->next=temp;
       }
       else//非第一次 ,将上一次的结点的指针域指向新结点
       {
         tail->next=temp;
       }
       tail=temp;//用于保存上一次建立的结点,用于中间值

    }
    tail->next=NULL;//将最后一个结点的指针域指向空
    return head;
}

int main(){  //主函数
	LinkList L = create_Link();
    L=L->next;
    while(L!=NULL)
    {
        printf("%d\n",L->data);
        L=L->next;
    }
	return 0;
}

这里我们新建了一个单链表 有5个结点 数据域存放的分别是 0 1 2 3 4

后面我们对单链表的操作都是对这个表进行操作

 2.6.3单链表的读取

概述:获取第i个元素的数据域的值

思路:声明一个结点p指向链表的第一个结点,初始化j从1开始

       :当j<i时,就遍历链表 让p的指针往后移动,不断指向下一结点,j累加1

       :若到链表未尾p为空,则说明第i个元素不存在

       :否则查找成功,返回结点p的数据。

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
typedef struct node{
	int data;
	struct node *next;
}node, *LinkList;  //结构体的定义

LinkList create_Link()
{  //创建单链表的主要代码(尾插法建立单链表)
	LinkList head = NULL;//建立一个头指针,用于指向第一个结点
	LinkList temp = NULL;
	LinkList tail = NULL;
    head=(LinkList)malloc(sizeof(node));//声明头指针的内存
    head->next=NULL;//将头指针指向空
    for(int i=0;i<5;i++)
    {
       temp=(LinkList)malloc(sizeof(node));//声明一块内存是用来存储新结点
       temp->next=NULL;//将新结点的指针指向空防止野指针
       temp->data=i;//新结点的数据域数据就是键入的值
       if(head->next==NULL)//当第一次插入的时候把头指针指向第一个结点
       {
         head->next=temp;
       }
       else//非第一次 ,将上一次的结点的指针域指向新结点
       {
         tail->next=temp;
       }
       tail=temp;//用于保存上一次建立的结点,用于中间值

    }
    tail->next=NULL;//将最后一个结点的指针域指向空
    return head;
}
int Getlist(LinkList L,int i)
{
    //i=3
    int j=0;
    LinkList p;//声明一个结点p
    p=L->next;//p指向链表l的第一个结点
    while( p&& j<i)//当p不为空 和j《i的时候循环直到p为空 或者j》=i
    {
        p=p->next;
        ++j;
    }
    if(!p || j>i)//如果上述判断条件是p为空或者j>i的时候 即错误返回0
      return 0;

    return p->data;//返回第i个元素的数据域值
}
int main(){  //主函数
	LinkList L = create_Link();
    printf("%d\n",Getlist(L,3));
	return 0;
}

2.6.4单链表的插入

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
typedef struct node{
	int data;
	struct node *next;
}node, *LinkList;  //结构体的定义

LinkList create_Link()
{  //创建单链表的主要代码(尾插法建立单链表)
	LinkList head = NULL;//建立一个头指针,用于指向第一个结点
	LinkList temp = NULL;
	LinkList tail = NULL;
    head=(LinkList)malloc(sizeof(node));//声明头指针的内存
    head->next=NULL;//将头指针指向空
    for(int i=0;i<5;i++)
    {
       temp=(LinkList)malloc(sizeof(node));//声明一块内存是用来存储新结点
       temp->next=NULL;//将新结点的指针指向空防止野指针
       temp->data=i;//新结点的数据域数据就是键入的值
       if(head->next==NULL)//当第一次插入的时候把头指针指向第一个结点
       {
         head->next=temp;
       }
       else//非第一次 ,将上一次的结点的指针域指向新结点
       {
         tail->next=temp;
       }
       tail=temp;//用于保存上一次建立的结点,用于中间值

    }
    tail->next=NULL;//将最后一个结点的指针域指向空
    return head;
}
int  listinsert(LinkList L,int i,int e)
{
    LinkList p,new;
    p=L;
    int j=1;
    while(p&&j<i)
    {
        p=p->next;
        ++j;
    }
    if(!p || j>i)
    return 0;//第i个元素不存在
    new=(LinkList)malloc(sizeof(node));//建立一块新的内存 存放结点
    new->data=e;//数据域赋值
    new->next=p->next;//将新结点的指针指向插入位置的下一个结点
    p->next=new;//将插入位置的上一个结点的指针域指向新结点
    return  1;
}
int main(){  //主函数
	LinkList L = create_Link();
    int a=listinsert(L,2,9);
    if(a)
    {
        L=L->next;
        while(L!=NULL)
        {
          printf("%d\n",L->data);
           L=L->next;
        }
    }
	return 0;
}

成功将9插入在第二个位置,这俩句是核心,一定要先将新结点的指针指向插入位置的下一个结点

    new->next=p->next;//将新结点的指针指向插入位置的下一个结点
    p->next=new;//将插入位置的上一个结点的指针域指向新结点

 2.6.5单链表的删除

这里我们需要注意我们要删除某个结点的时候我们将指针指向删除结点的上一个结点

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
typedef struct node{
	int data;
	struct node *next;
}node, *LinkList;  //结构体的定义

LinkList create_Link()
{  //创建单链表的主要代码(尾插法建立单链表)
	LinkList head = NULL;//建立一个头指针,用于指向第一个结点
	LinkList temp = NULL;
	LinkList tail = NULL;
    head=(LinkList)malloc(sizeof(node));//声明头指针的内存
    head->next=NULL;//将头指针指向空
    for(int i=0;i<5;i++)
    {
       temp=(LinkList)malloc(sizeof(node));//声明一块内存是用来存储新结点
       temp->next=NULL;//将新结点的指针指向空防止野指针
       temp->data=i;//新结点的数据域数据就是键入的值
       if(head->next==NULL)//当第一次插入的时候把头指针指向第一个结点
       {
         head->next=temp;
       }
       else//非第一次 ,将上一次的结点的指针域指向新结点
       {
         tail->next=temp;
       }
       tail=temp;//用于保存上一次建立的结点,用于中间值

    }
    tail->next=NULL;//将最后一个结点的指针域指向空
    return head;
}
int  listdelete(LinkList L,int i)
{
    LinkList p,q;
    p=L;
    int j=1;
    while(p->next&&j<i)
    {
        p=p->next;
        ++j;
    }
    if(!p || j>i)
    return 0;//第i个元素不存在
    q=p->next;//将要删除的结点赋值给q
    p->next=q->next;//将指向删除结点的指针指向删除结点的下一个结点
    free(q);//释放删除结点的内存空间
    return  1;
}
int main(){  //主函数
	LinkList L = create_Link();
    int a=listdelete(L,2);
    if(a)
    {
        L=L->next;
        while(L!=NULL)
        {
          printf("%d\n",L->data);
           L=L->next;
        }
    }
	return 0;
}

2.6.6链表的整表删除

将每个结点的内存空间都进行释放

int  listclear(LinkList L,int i)
{
    LinkList p,q;
    p=L->next;
    while(p)
    {
        q=p->next;
        free(p);
        p=q;
    }
    L->next=NULL;
    return  1;
}

最后我们将单链表和顺序链接做一下对比

存储分配方式:顺序存储结构用一段连续的存储单元依次存储线性表的数据元素 利用数组

                      :单链表采用链式存储结构,用一组任意的存储单元存放线性表的元素 利用指针

空间性能:顺序存储结构需要预分配存储空间,分大了 浪费 分小了 易发生上溢

              :单链表不需要分配存储空间只要有就可以分配,元素个数也不受限制

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/399599.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Day51 42 接雨水 84柱状图中的最大矩形

42 接雨水 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1]输出&#xff1a;6解释&#xff1a;上面是由数组 [0,1,0,2,1,0,1,3,…

前端 webSocket 的使用

webSocket使用 注意要去监听websocket 对象事件&#xff0c;处理我们需要的数据 我是放在了最外层的index 内&#xff0c;监听编辑状态&#xff0c;去触发定义的方法。因为我这个项目是组件化开发&#xff0c;全部只有一个总编辑按钮&#xff0c;我只需监听是否触发了编辑即可…

深入探索STM32的存储选项:片内RAM、片内Flash与SDRAM

博客&#xff1a;深入探索STM32的存储选项&#xff1a;片内RAM、片内Flash与SDRAM 在嵌入式系统设计中&#xff0c;存储管理是一个至关重要的方面&#xff0c;尤其是对于基于STM32这类强大的微控制器来说。STM32系列微控制器因其高性能、低功耗以及灵活的存储选项而广受欢迎。本…

数智化转型的三大关键点

一、重新认识数智化转型 消费红利时代&#xff0c;伴随中国宏观经济向好发展&#xff0c;相当一部分企业可以轻松实现快速增长&#xff0c;如同搭乘了一架高速运转的电梯一路飞升。然而&#xff0c;随着宏观经济增速放缓&#xff0c;时代的电梯逐渐失去效力&#xff0c;中国商…

18. 四数之和 - 力扣(LeetCode)

问题描述 给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] &#xff08;若两个四元组元素一一对应&#xff0c;则认为两个四元组重复&#xff09;&#xff1a; …

C#,洗牌问题(Card Shuffle Problem)的算法与源代码

1 洗牌问题&#xff08;Card Shuffle Problem&#xff09; 洗牌问题&#xff08;Card Shuffle Problem&#xff09;的基本描述 你有 100 张牌&#xff0c;从 1 到 100。 你把它们分成 k 堆&#xff0c;然后按顺序收集回来。 例如&#xff0c;如果您将它们分成 4 堆&#xff0…

关于发送邮件时Reply Reply All和Forward的区别

我们发送邮件的时候总是会纠结到底是用回复&#xff0c;还是回复全部&#xff0c;还是转发。 回复- 仅回复发件人。 全部回复- 回复发件人和抄送/密件抄送的联系人。 转发- 将电子邮件的副本发送给其他收件人。 这几种情形分别在什么时候用呢&#xff1f; 回复 比如Alen给你…

【设计模式】23种设计模式笔记

设计模式分类 模板方法模式 核心就是设计一个部分抽象类。 这个类具有少量具体的方法&#xff0c;和大量抽象的方法&#xff0c;具体的方法是为外界提供服务的点&#xff0c;具体方法中定义了抽象方法的执行序列 装饰器模式 现在有一个对象A&#xff0c;希望A的a方法被修饰 …

浏览器缓存机制

浏览器缓存有先后顺序&#xff0c;总体分为以下四个方面&#xff1a; Memory Cache Service Worker Cache Disk Cache Push Cache 缓存位置 1. Memory Cache 内存中的缓存 优点&#xff1a; 浏览器会优先去命中的一种缓存 响应速度最快 缺点&#xff1a; 缓存时间短&#xf…

ABAP 导入Excel表示例程序

目录 ABAP 导入excel示例程序创建程序使用的结构上传下载模板 ABAP 导入excel示例程序 批量导入程序&#xff0c;需要使用到导入模板&#xff0c;首先需要创建程序&#xff0c;之后是需要创建excel导入模板&#xff0c;并且需要将excel导入模板上传到SAP系统里面&#xff0c;之…

Code-Audit(代码审计)习题记录

介绍&#xff1a; 自己懒得搭建靶场了&#xff0c;靶场地址是 GitHub - CHYbeta/Code-Audit-Challenges: Code-Audit-Challenges为了方便在公网练习&#xff0c;可以随地访问&#xff0c;本文所有的题目均来源于网站HSCSEC-Code Audit 1、习题一 题目内容如下&#xff1a; 1…

运行jar时提示缺少依赖的类

供应商丢过来一个jar&#xff0c;是用Java写的Windows桌面程序&#xff0c;运行jar时提示缺少依赖的类&#xff0c;一看就是打包没带依赖的库&#xff0c;下面是解决方法&#xff1a; 1、解压缩jar&#xff0c;查看 META-INF 目录下的 MANIFEST.MF&#xff0c;看看都引用了哪些…

FlinkCDC详解

1、FlinkCDC是什么 1.1 CDC是什么 CDC是Chanage Data Capture&#xff08;数据变更捕获&#xff09;的简称。其核心原理就是监测并捕获数据库的变动&#xff08;例如增删改&#xff09;&#xff0c;将这些变更按照发生顺序捕获&#xff0c;将捕获到的数据&#xff0c;写入数据…

Cesium for Unreal 从源码编译到应用——插件编译

一、安装环境 Unreal Engine 5.3 CMake 3.17.5 Microsoft Visual Studio 2019 二、源码准备 下载cesium-unreal-samples工程。 git clone https://github.com/CesiumGS/cesium-unreal-samples.git 然后在工程目录创建Plugins文件夹&#xff0c;并下载cesium-unreal工程。 …

演练纪实│同创永益助力中交财务公司成功开展灾备应急演练

12月26日同创永益协助中交财务公司顺利完成核心业务系统、高性能云平台等关键业务系统的子系统验证演练&#xff0c;以及模拟切换演练、同城灾备应急演练。 同创永益北方交付二组的同事在演练前与中交财务公司演练负责人紧密沟通&#xff0c;展现了出色的专业素养和团队协作能力…

Vue3学习——标签的ref属性

在HTML标签上&#xff0c;可以使用相同的ref名称&#xff0c;得到DOM元素ref放在组件上时&#xff0c;拿到的是组件实例&#xff08;组件defineExpose暴露谁&#xff0c;ref才可以看到谁&#xff09; <script setup lang"ts"> import RefPractice from /compo…

测试C#调用Emgucv读取并显示视频文件

微信公众号“CSharp编程大全”的文章《C# 视频播放》介绍了基于emgucv读取视频文件并播放的用法&#xff08;百度文章名没有找到对象的文章地址&#xff0c;有兴趣的可以在微信中搜索该公众号并查看文章具体内容&#xff09;&#xff0c;本文学习并测试Emgucv播放视频文件的基本…

拼多多关键字搜索API-通过关键字获取拼多多商品列表商品价格商品id商品链接

拼多多根据关键词取商品列表 API 返回值说明 item_search-根据关键词取商品列表 公共参数 请求地址: pinduoduo/item_search 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameString是API接口名称&…

Mysql系列之命令行登录、连接工具登录、数据库表常用命令

登录与常用命令 连接工具登录命令行登录数据库1、查看数据库2、指定数据库3、查看当前数据库4、建库语句 数据表1、查看数据表2、查看表结构信息3、查看建表语句4、建表语句 连接工具登录 首先下载mysql连接工具&#xff0c;解压后直接打开软件&#xff0c;按以下步骤操作&…

☀️将大华摄像头画面接入Unity 【2】配置Unity接监控画面

一、前言 上一篇咱们将大华摄像头接入到电脑上了&#xff0c;接下来准备接入到unity画面。 接入到监控就涉及到各种视频流的格式rtsp、rtmp、m3u8。 Unity里有一些播放视频流的插件&#xff0c;主要的就是AVPro Video 和 UMP等&#xff0c;这次我用的是UMP 最好使用2.0.3版本…