【数据结构】链表的实现以及应用

2023-11-10

目录

1.链表

1.1概念及结构

1.2链表的实现

1.2.1接口

1.2.2创建链表

1.2.3创建新的节点

1.2.4头插

1.2.5尾插

1.2.6头删

1.2.7尾删

1.2.8查询

1.2.9在pos指针之前插入数据

1.2.10删除pos位置的数据

2.链表的应用 

2.1两条有序链表的归并

2.2 正负分离

3.源代码

头文件Slist.h

接口文件SList.c

测试文件test.c


1.链表

1.1概念及结构

        链表,别名链式存储结构或单链表,用于存储逻辑关系为 "一对一" 的数据。与顺序表不同,链表不限制数据的物理存储状态,换句话说,使用链表存储的数据元素,其物理存储位置是随机的。

        上一篇文章介绍了顺序表,然而顺序表是存在许多缺陷的,比如:每一次插入和删除数据,都会造成大量元素移动,导致时间的效率低下。为了改进顺序存储结构的缺点,引入了链式存储结构,即链表。但因为链式存储结构的存储单元不连续,所以需要通过指针来访问它的后续元素。

         链表中的每个节点是一个结构体,其有两个成员,一个成员存储数据,被称为数据域,另一个成员存储指向下一个节点的指针,被称为指针域。如下图。

 

         n个节点构成一个链表,即为线性表的链式存储结构。其逻辑结构我们认为是这样的,如下:

        但是链表在内存中的实际存储情况却并不是如此,即其物理结构并不这样。由于链表的节点是需要的时候再在动态内存中开辟,所以节点在内存中的位置并不是连续的,而是随机的,如下:

1.2链表的实现

        链表分为有哨兵位和无哨兵位的区别,如下图,p1指向有哨兵位的链表的哨兵节点,p2指向无哨兵位的链表的首节点。哨兵位不存储数据,只是起到一个过渡作用,但是实际应用中无哨兵位的链表使用比较多,所以本文主要介绍无哨兵位的链表:

1.2.1接口

        一个完整的链表功能无疑是强大的,可以进行插入、删除、创建节点、查询、清空链表等等。在这里和顺序表的区别是,链表内除了打印这个接口,其他接口涉及到链表的都要传二级指针,原因下面会做出解释。

1.2.2创建链表

         首先要创建一个链表,与顺序表同理,typedef int SLTDataType; 的原因是:为了方便更改链表中存储的数据类型。比如原本链表的数据域是存储int类型的数据,但是此时想要改成double类型的数据,只需要把 typedef int SLTDataType; 改成 typedef double SLTDataType; 就可以了。

        同时,对于指针域。指针指向的类型是 struct SListNode ,如上面链表结构的图片,不难看出,指针域存储的是下一个节点的位置,而每一个节点的类型就是 struct SListNode,所以是这样。此外,指针域不能写成  SLTNode *next; 会导致出错。

typedef int SLTDataType;

typedef struct SListNode
{
	SLTDataType data;        //数据域
	struct SListNode* next;  //指针域
}SLTNode;

1.2.3创建新的节点

        如下,开辟一个节点的空间,成功后,该节点数据与存储要添加的数据,指针域为空。返回指向新节点的指针,否则无法找到新节点。

SLTNode* SListBuySLTNode(SLTDataType x)
{
	SLTNode* temp = (SLTNode*)malloc(sizeof(SLTNode));
	if (temp == NULL)
	{
		printf("malloc error!");
		exit(-1);
	}
	//数据域存储添加的数据,指针域为空
	temp->data = x;
	temp->next = NULL;
	return temp;
}

1.2.4头插

        由于是无哨兵位的节点,所以不需要初始化。直接进行插入、删除等操作即可。为什么要传二级指针,下面有解释。

void SListPushFront(SLTNode** phead, SLTDataType x)
{
	assert(phead);
	SLTNode* newnode= SListBuySLTNode(x);
	newnode->next = *phead;
	*phead = newnode;
}

        如上图,假设原本链表的头节点叫A节点,新插入的头节点叫New节点。头插操作要先创建一个新节点,这个New节点用newnode指针接收,然后New节点的指针域要指向A节点,最后再把p指针指向New节点。在这里,p指针被修改了,要在函数内部修改传入的指针,就得传二级指针

        如下,如果传的一级指针,由于形参是对实参的一份临时拷贝,所以,如果把p指针当作实参传给SListPushFront函数,那么函数内部的指针变量phead,其指向的地址和p指针是一样的。但是如果修改phead,那么只是修改了形参里面的phead,使得形参不再指向A节点,但是函数调用结束之后,实参p指针依然不变。

         但是,如果传的参数是二级指针,即传入 &p 。那么就可以对p指针进行修改。此时phead代表的是实参p的地址,phead指针指向的是实参p,即*phead就是p,所以在函数里面把新节点的指针域指向*phead。如果要修改p,那么  *phead=……; 就可以达到目的。

         这就好比于普通的传参,假设有一个函数 test(int x,int y);  然后有 变量 int a=6,  b=10;  调用函数test(a,b); 那么在这个函数运行之后,无论函数内部的x、y如何改变,变量a、b依然不变。但是如果是 test1(int *x,int*y); 调用函数test1(&a,&b);   此时在函数内部改变 *a、*b,那么a、b就会被改变。 

        把普通传参看成  test(int *x,int *y);   int * a,* b; 调用函数test(a,b); 是无法改变a、b的,只有test1(int **x,**y);    调用函数 test1(&a,&b);  在函数内部更改*x 、*y,才会改变函数外部的a、b。

1.2.5尾插

        尾插有两种情况,第一钟是链表无数据的情况,此时*phead指针指向NULL(即上图中的p指针指向NULL)。那么直接把*phead改成新生成的节点的地址即可。原理同上。

        第二种情况是链表有数据的情况下,直接把新节点插入到最后一个节点后面,即把当前最后一个节点的指针域指向新节点。所以先找到最后一个节点的地址,然后把它的指针域改成新生成的节点地址即可。

void SListPushBack(SLTNode** phead, SLTDataType x)
{
	//断言
	assert(phead);
	SLTNode* newnode = SListBuySLTNode(x);
	SLTNode* temp = *phead;
	//没有数据的情况下
	if (*phead == NULL)
	{
		*phead = newnode;
	}
	//有数据的情况下
	else
	{
		while (temp->next != NULL)
		{
			temp = temp->next;
		}
		temp->next = newnode;
	}
}

1.2.6头删

        头删也分为两种情况:第一种,链表为空,这种情况直接用assert()避免。

        第二种,链表不为空。使原本指向第一个节点的指针,指向第二个节点即可。因为要改变指针,所以函数要传二级指针,和头插类似原理,如下图。当然直接*phead=(*phead)->next;也可以。

void SListPopFront(SLTNode** phead)
{
	assert(*phead != NULL);

	SLTNode* temp = *phead;
	*phead = temp->next;
	free(temp);
	temp = NULL;
}

1.2.7尾删

        尾删有三种情况:第一种,没有节点,也是用assert()来判断。

        第二种,只有一个节点,此时直接free掉*phead指向的空间即可,在这里用的temp和*phead指向的地址相同,所以直接free(temp);也是同样的效果,然后置*phead=NULL;

        第三种,链表中有多个节点。此时要删掉最后一个节点,那么应该提前找到倒数第二个节点,然后释放尾节点,并把倒数第二个节点的指针域指向NULL。重点就在于找到倒数第二个节点,只需要设置一个prev指针,使得这个指针比temp指针慢一个节点的位置即可,思想如下图。循环结束之后,prev就指向了倒数第2个节点。

        看完第三种情况,就对为什么尾删要单独考虑只有一个节点的情况,而头删不需要考虑这种情况有所了解了。尾删要设置一个prev指针,当只有一个节点的情况下,prev指针 按照第三种情况的算法 无法指向任何一个节点,所以将其单独考虑。而头删则不用这样,如果只有一个节点,那么该节点指针域就是NULL,按照头删的算法,*phead指向NULL,然后第一个节点被释放,故不需要额外考虑。

void SListPopBack(SLTNode** phead)
{
	//断言
	assert(*phead != NULL);
	SLTNode* temp = *phead;
	//只有一个数据的情况下,free*phead且*phead要置成空
	if (temp->next == NULL)
	{
		free(temp);
		*phead = NULL;
	}
	//多个数据下,新的是最后一个数据的指针域要为空
	else
	{
		SLTNode* prev = NULL;
		while (temp->next != NULL)
		{
			prev = temp;
			temp = temp->next;
		}
		free(temp);
		prev->next = NULL;
	}
}

1.2.8查询

        相较于头删和尾删,查询数据的算法就非常容易了。首先考虑链表为空的情况,assert()判断。其次,如果不为空,遍历链表,找到对应数据则返回该指向节点的指针,遍历完了还是没有返回对应指针的话,那就没有找到,返回NULL。

//查询数据
SLTNode* SListFind(SLTNode* phead, SLTDataType x)
{
	//断言
	assert(phead != NULL);

	SLTNode* temp = phead;
	while(temp != NULL)
	{
		if (temp->data == x)
			return temp;
		else
			temp = temp->next;
	}
	return NULL;
}

1.2.9在pos指针之前插入数据

        这里在pos指针之前插入,而不是第几个数据之前插入或者在某个特定数据之前插入的原因是,查询数据这个功能返回值是该数据的指针,在调用这个函数之前,先查询目的节点的指针,然后直接调用这个函数。

        这个接口函数要考虑到,查询接口如果没有找到数据,会返回NULL,所以一开始assert()直接判断,如果pos为假(即pos==NULL)就会报错。

        如果pos是指向头节点,就相当于头插

        如果pos是指向其他节点,那么先找到该节点的前面一个节点prev,如下算法,while循环结束之后,prev->next == pos。此时要先让新节点的指针域指向pos,然后把prev节点的指针域指向新节点,如下图第一种插入方法。(当然有了这种插入算法,头插和尾插其实都可以被替代,因为它同时也具有这两种功能。)

        这里就有一个有意思的小问题,为什么一定要先让新节点指针域指向pos的位置呢,不可以先让prev的指针域指向新节点嘛?  在这里,是可以的,因为函数第2个参数就是pos。但是,如果是在别的情况下,传入的参数不是pos,而是pos节点的数据,那么将prev->next指向新节点之后,我们就无法找到pos指向的节点了,链表在这里就断了,如下第二种插入方法。(当然,第二种插入方法,可以在prev->next指向新节点之前,设计一个新指针,指向prev->next,即指向pos,就相当于把pos位置存下来了,只是这样太麻烦,不如直接第一种插入方法)

//在pos位置之前插入数据
void SListInsert(SLTNode** phead, SLTNode* pos, SLTDataType x)
{
	//断言,括号里的条件为假的时候报错,phead==NULL即假
	assert(phead && pos);

	SLTNode* newnode = SListBuySLTNode(x);
	//pos是头节点的情况
	if (*phead == pos)
	{
		newnode->next = *phead;
		*phead = newnode;
	}
	else
	{
		SLTNode* prev = *phead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		newnode->next = pos;
		prev->next=newnode;
	}

}

1.2.10删除pos位置的数据

        这里要分为三种情况,空链表,通过断言判断。

        如果pos指向头节点,那么头节点被删除,链表为空,直接*phead指向pos->next(NULL),然后free(pos);即可。

        除了以上两种情况,就得找到pos节点的前一个节点,用prev指向该节点,然后把该节点的指针域指向pos节点的后一个节点,即prev->next=pos->next。然后free(pos);

//在中间删除数据
void SListErase(SLTNode** phead, SLTNode* pos)
{
	//空链表,断言
	assert(*phead);
	//删除头节点
	if (pos == *phead)
	{
		*phead =pos->next;
	}
	else
	{
		SLTNode* prev = *phead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
	}
	free(pos);
}

2.链表的应用 

2.1两条有序链表的归并

        两条链表的归并。和顺序表的归并异曲同工,具体可见上一篇博客。

void merge(SLTNode* a, SLTNode* b, SLTNode** ret)
{
	SLTNode* temp1 = a;
	SLTNode* temp2 = b;
	while (temp1 != NULL && temp2 != NULL)
	{
		if (temp1->data < temp2->data)
		{
			SListPushBack(ret, temp1->data);
			temp1 = temp1->next;
		}
		else
		{
			SListPushBack(ret, temp2->data);
			temp2 = temp2->next;
		}
	}
	if (temp1)//temp2归并完了
	{
		while (temp1)
		{
			SListPushBack(ret, temp1->data);
			temp1 = temp1->next;
		}
	}
	else//temp1归并完了
	{
		while (temp2)
		{
			SListPushBack(ret, temp2->data);
			temp2 = temp2->next;
		}
	}
	return;
}

2.2 正负分离

         输入N个数字,存在链表中,将这个链表中的正负元素分开,且保持相对位置不变。

        这题做法也不难,创建一个有N个数据的数组,然后两次遍历链表,第一次找出小于0的,然后拷贝到新数组,第二次找出大于0的,拷贝到新数组。然后把新数组的数据拷贝到链表中即可。

//正负分开,负数在前面,正数在后面,相对位置不变
void test2(SLTNode** a)
{
	//输入数据
	int x = 0;
	for (int i = 0;i < N;i++)
	{
		scanf("%d", &x);
		SListPushBack(a, x);
	}
	//操作,arr存储数据
	int count = 0;
	SLTDataType arr[N] = { 0 };
	SLTNode* temp = *a;
	while (temp)
	{
		if (temp->data < 0)
			arr[count++] = temp->data;
		temp = temp->next;
	}
	temp = *a;
	while (temp)
	{
		if (temp->data > 0)
			arr[count++] = temp->data;
		temp = temp->next;
	}
	//重新赋值
	count = 0;
	temp = *a;
	while (temp)
	{
		temp->data = arr[count++];
		temp = temp->next;
	}
	return;
}

3.源代码

头文件Slist.h

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#define N 10
typedef int SLTDataType;

typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;
}SLTNode;

//打印
void SListPrint(SLTNode* phead);
//创建新的单元
SLTNode* SListBuySLTNode(SLTDataType x);
//头插
void SListPushFront(SLTNode** phead,SLTDataType x);
//尾插
void SListPushBack(SLTNode** phead, SLTDataType x);
//头删
void SListPopFront(SLTNode** phead);
//尾删
void SListPopBack(SLTNode** phead);
//查询数据
SLTNode* SListFind(SLTNode* phead, SLTDataType x);
//在pos位置之前插入数据
void SListInsert(SLTNode** phead, SLTNode* pos, SLTDataType x);
//在中间删除数据
void SListErase(SLTNode** phead, SLTNode* pos);
//清空链表
void SListDestory(SLTNode** phead);

//归并
void merge(SLTNode* a, SLTNode* b, SLTNode** ret);
//正负分离
void test2(SLTNode** a);

接口文件SList.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"

void SListPrint(SLTNode* phead)
{
	//断言,无数据直接报错
	assert(phead != NULL);
	SLTNode* temp = phead;
	while (temp != NULL)
	{
		printf("%d->", (*temp).data);
		temp = (*temp).next;
	}
	printf("\n");

}

SLTNode* SListBuySLTNode(SLTDataType x)
{
	SLTNode* temp = (SLTNode*)malloc(sizeof(SLTNode));
	if (temp == NULL)
	{
		printf("malloc error!");
		exit(-1);
	}
	//数据域存储添加的数据,指针域为空
	temp->data = x;
	temp->next = NULL;
	return temp;
}

void SListPushFront(SLTNode** phead, SLTDataType x)
{
	assert(phead);
	SLTNode* newnode= SListBuySLTNode(x);
	newnode->next = *phead;
	*phead = newnode;
}

void SListPushBack(SLTNode** phead, SLTDataType x)
{
	//断言
	assert(phead);
	SLTNode* newnode = SListBuySLTNode(x);
	SLTNode* temp = *phead;
	//没有数据的情况下
	if (*phead == NULL)
	{
		*phead = newnode;
	}
	//有数据的情况下
	else
	{
		while (temp->next != NULL)
		{
			temp = temp->next;
		}
		temp->next = newnode;
	}
}

void SListPopFront(SLTNode** phead)
{
	assert(*phead != NULL);

	SLTNode* temp = *phead;
	*phead = temp->next;
	free(temp);
	temp = NULL;
}

void SListPopBack(SLTNode** phead)
{
	//断言
	assert(*phead != NULL);
	SLTNode* temp = *phead;
	//只有一个数据的情况下,free*phead且*phead要置成空
	if (temp->next == NULL)
	{
		free(temp);
		*phead = NULL;
	}
	//多个数据下,新的是最后一个数据的指针域要为空
	else
	{
		SLTNode* prev = NULL;
		while (temp->next != NULL)
		{
			prev = temp;
			temp = temp->next;
		}
		free(temp);
		prev->next = NULL;
	}
}

//查询数据
SLTNode* SListFind(SLTNode* phead, SLTDataType x)
{
	//断言
	assert(phead != NULL);

	SLTNode* temp = phead;
	while(temp != NULL)
	{
		if (temp->data == x)
			return temp;
		else
			temp = temp->next;
	}
	return NULL;
}
//在pos位置之前插入数据
void SListInsert(SLTNode** phead, SLTNode* pos, SLTDataType x)
{
	//断言,括号里的条件为假的时候报错,phead==NULL即假
	assert(phead);

	SLTNode* newnode = SListBuySLTNode(x);
	//pos是头节点的情况
	if (*phead == pos)
	{
		newnode->next = *phead;
		*phead = newnode;
	}
	else
	{
		SLTNode* prev = *phead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		newnode->next = pos;
		prev->next=newnode;
	}

}
//在中间删除数据
void SListErase(SLTNode** phead, SLTNode* pos)
{
	//空链表,断言
	assert(*phead);
	//删除头节点
	if (pos == *phead)
	{
		*phead =pos->next;
	}
	else
	{
		SLTNode* prev = *phead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
	}
	free(pos);
}
void SListDestory(SLTNode** phead)
{
	//断言,空链表不需要销毁
	assert(*phead);

	SLTNode* temp = *phead;
	SLTNode* cur = *phead;
	while (temp!=NULL)
	{
		cur = temp->next;
		free(temp);
		temp = cur;
	}
	*phead = NULL;
}

//归并
void merge(SLTNode* a, SLTNode* b, SLTNode** ret)
{
	SLTNode* temp1 = a;
	SLTNode* temp2 = b;
	while (temp1 != NULL && temp2 != NULL)
	{
		if (temp1->data < temp2->data)
		{
			SListPushBack(ret, temp1->data);
			temp1 = temp1->next;
		}
		else
		{
			SListPushBack(ret, temp2->data);
			temp2 = temp2->next;
		}
	}
	if (temp1)//temp2归并完了
	{
		while (temp1)
		{
			SListPushBack(ret, temp1->data);
			temp1 = temp1->next;
		}
	}
	else//temp1归并完了
	{
		while (temp2)
		{
			SListPushBack(ret, temp2->data);
			temp2 = temp2->next;
		}
	}
	return;
}

//正负分开,负数在前面,正数在后面,相对位置不变
void test2(SLTNode** a)
{
	//输入数据
	int x = 0;
	for (int i = 0;i < N;i++)
	{
		scanf("%d", &x);
		SListPushBack(a, x);
	}
	//操作,arr存储数据
	int count = 0;
	SLTDataType arr[N] = { 0 };
	SLTNode* temp = *a;
	while (temp)
	{
		if (temp->data < 0)
			arr[count++] = temp->data;
		temp = temp->next;
	}
	temp = *a;
	while (temp)
	{
		if (temp->data > 0)
			arr[count++] = temp->data;
		temp = temp->next;
	}
	//重新赋值
	count = 0;
	temp = *a;
	while (temp)
	{
		temp->data = arr[count++];
		temp = temp->next;
	}
	return;
}

测试文件test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"
void Test1()
{
	SLTNode* a=NULL;//一开始没有初始化为NULL,导致pushback里面最后一种情况,传过去的a是野指针,随意指,不是结构体的
	SListPushBack(&a, 5);
	SListPushBack(&a, 8);
	SListPushBack(&a, 456);
	//SListPopBack(&a);
	SListPushFront(&a, 5);
	SListPushFront(&a, 8);
	SListPushFront(&a, 456);
	SListPopFront(&a);
	SListPrint(a);
}


void Test2()
{
	SLTNode* a = NULL;
	SListPushBack(&a, 5);
	SListPushBack(&a, 8);
	SListPushBack(&a, 456);
	//SListPopBack(&a);
	SListPushFront(&a, 5);
	SListPushFront(&a, 8);
	SListPushFront(&a, 456);
	SListPopFront(&a);

	SLTNode* b=SListFind(a, 8);
	//SListInsert(&a, b, 20);

	SListErase(&a,b);
	SListPrint(a);

	SListDestory(&a);
	//SListPrint(a);
}
int main()
{
	//Test1();
	//Test2();

	//(1)归并
	//初始化la
	SLTNode* la = NULL;
	SListPushBack(&la, 1);
	SListPushBack(&la, 3);
	SListPushBack(&la, 6);
	SListPushBack(&la, 9);
	SListPushBack(&la, 10);
	SListPrint(la);
	//初始化lb
	SLTNode* lb = NULL;
	SListPushBack(&lb, 2);
	SListPushBack(&lb, 3);
	SListPushBack(&lb, 4);
	SListPushBack(&lb, 5);
	SListPushBack(&lb, 6);
	SListPushBack(&lb, 8);
	SListPushBack(&lb, 14);
	SListPrint(lb);

	SLTNode* ret = NULL;
	merge(la, lb, &ret);
	printf("归并后的结果是:");
	SListPrint(ret);
	printf("\n");

	////(2)正负
	//SLTNode* p1 = NULL;
	//test2(&p1);
	//printf("正负分开后的结果:");
	//SListPrint(p1);

	return 0;
}

        如果本文对你有所帮助,可以三连支持!!!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【数据结构】链表的实现以及应用 的相关文章

  • 前端搭建砸地鼠游戏(内附源码)

    The sand accumulates to form a pagoda 写在前面 功能介绍 页面搭建 样式设置 逻辑部分 完整代码 写在前面 上周我们实通过前端基础实现了打字通 当然很多伙伴再评论区提出了想法 后续我们会考虑实现的 今天
  • Vue+ElementUI中表格嵌套的使用方法

    需求 在开发中会遇到很多表格嵌套表格的使用 一个父级表格通过展开行要打开子级的表格 如果利用官网中的展开行的方式去实现其实是有点困难的 首先实现行展开 这个是用到了elementUI中的一个属性通过设置 type expand 和 Scop
  • WebSocket 协议简介

    一 什么是WebSocket WebSocket是一种在单个TCP连接上进行全双工通讯的协议 WebSocket通信协议于2011年被IETF定为标准RFC 6455 并由RFC7936补充规范 WebSocket API也被W3C定为标准
  • 为什么 Mac 适合编程?

    强劲的 GPU 和 CPU 我的家用电脑和笔记本都配了顶级的显示器和 GPU Steam 上有 2000 游戏 我和孩子玩了很多 并且我对 CUDA 和 深度学习很感兴趣 而 Mac 对此就无能为力了 对我来说 强大的 GPU 是非常重要的
  • 整体结构&InnoDB数据字典(1) --系统表空间结构(三十三)

    前面说了xdes 类型页面 第一页的extent0里面的xdes页面叫做fsp 因为里面还存着表空间的数据file space header 这里面主要存着xdes entry几个链表的基点 和inode entry链表的基点 后面的每个组
  • Crypto

    数据库密码 flag格式moctf 密码 题目如下 20岁的小刚 自幼热爱信息安全 一天他利用SQL注入漏洞入侵了XX公司的数据库 修改了数据库访问密码 网警不久便抓获了小刚 在小刚系统中发现了他做了入侵记录 上面写着一串字符串 D8EA7

随机推荐

  • ompl库安装使用

    参考官网 参考2 官网翻译
  • 期货交易入门知识有哪些?

    期货交易入门知识有哪些 期货初学者基本知识之二 期货交易方式 期货交易使用保证金交易制度 不同期货品种的保证金比例不同 并且同一种期货的保证金比例也是会变化和调整的 因此投资者需要注意自己所投资的期货品种的保证金比例是多少 另外 国内期货交
  • np.argmax()

    np argmax 表示返回索引最大值 需要区分索引和元素值 先说结论 三维张量时 将张量看成正方体 axis 0时表示沿高度轴 深度方向 各元素最大值 返回结果形状与正方体顶面相同 axis 1时表示沿宽度轴 矩阵行方向 各元素最大值 返
  • mysql怎么在一段时间区间内按照周分组,把属于一周的数据汇总在一起以及如何自定义周?

    小编在写需求的时候发现有一个需求是给出一个开始 起止时间 需要你把每一周的数据汇总然后展现出来 比如给了前四周的日期 需要你自动汇总第一周到第四周的数据 奇葩的来了 正常来说周一到周日为一周 或者周日到周六为一周 但是产品需要周六到周五算一
  • IDEA(2021) 创建Java Web项目

    IDEA 2021 创建Java Web项目 开发工具与关键技术 IDEA 2021 配置Tomcat 字符编码UTF 8 和热加载 作者 刘铭聪 撰写时间 2021年4月29日 1 IDEA 2021 创建Java Web项目 1 1 创
  • SolidWorks不能使用选择如允许此选择将生成有冲突的前后关系

    SolidWorks不能使用选择如允许此选择将生成有冲突的前后关系 1 SolidWorks不能使用选择如允许此选择将生成有冲突的前后关系 1 SolidWorks不能使用选择如允许此选择将生成有冲突的前后关系 https www swrj
  • Ubuntu 20.04安装LAMP,并配置sqli-labs靶场

    Ubuntu 20 04安装LAMP 并配置sqli labs靶场 一 安装lamp 安装apache2 安装php 安装mysql 安装libapache2 mod php 安装php mysql 确认安装成功 确认apache2安装成功
  • webpack serve 正常启动但没效果

    安装 webpack dev server npm i webpack dev server d 都是最新版本 配置webpack config js const path require path 导入 node js 中专门操作路径的模
  • 阿里云E-HPC+i4p大内存实例,加速寻因生物单细胞数据分析效率

    寻因生物是一家专注于单细胞技术的生物科技企业 拥有自主研发的微孔芯片与油包水双技术单细胞捕获平台 致力于通过国产高通量单细胞全链条产品及服务 将单细胞技术普适化应用于临床及药物研发 助力相关疾病研究 单细胞测序技术呼唤更高计算性能 自单细胞
  • 时序预测

    时序预测 MATLAB实现BiLSTM时间序列预测 目录 时序预测 MATLAB实现BiLSTM时间序列预测 BiLSTM介绍 BiLSTM实现 单层BiLSTM设计 多层BiLSTM设计 BiLSTM程序 设计案例1 设计案例2 参考资料
  • 【Vue2】事件处理(点击、按键)

    事件处理 js中button按钮点击触发函数写法
  • 贪心算法——最小生成树

    设G V E 是无向连通带权图 即一个网络 E中的每一条边 v w 的权为c v w 如果G的子图G 是一棵包含G的所有顶点的树 则称G 为G的生成树 生成树上各边权的总和称为生成树的耗费 在G的所有生成树中 耗费最小的生成树称为G的最小生
  • 【论文笔记_知识蒸馏_2021】KNOWLEDGE DISTILLATION VIA SOFTMAX REGRESSION

    代码地址 https github com jingyang2017 KD SRRL 摘要 本文通过知识提炼解决了模型压缩的问题 我们主张采用一种优化学生网络倒数第二层的输出特征的方法 因此与表征学习直接相关 为此 我们首先提出了一种直接的
  • vue使用element el-table实现动态表头数据表、并滚动展示列表

    vue使用el table 实现动态数据表 并滚动展示 tableLabel表头数据 支持条数 宽度设置 tableData存放所有数据 showList存放用于展示的数据 使用setInterval定时更新showList 删除第一条 末
  • R语言使用长短期记忆网络(LSTM)进行时间序列分析

    目录 引言 数据准备 数据预处理 创建训练数据和标签 构建LSTM模型 训练模型
  • Maven 多项目依赖版本管理(Maven Multi Project version management)

    博主工作负责的微服务已经达到了十几个 每个微服务相互直接都是独立的 有独立的project 于是maven的依赖管理就很头疼了 每一个项目都需要添加自己的版本 还要互相比对 每次升级一些第三方依赖需要修改所有文件 于是产生了自己写paren
  • 换脸-DeepFakeLab-SimSwap对比

    DeepFakeLab 主流的换脸模型DeepFakeLab只能实现一对一的换脸 用source数据集和target数据集对模型进行训练 训练后的模型只能将source的脸换到target上面去 其主要训练流程如下 训练一个encoder
  • 【ROS】TF2坐标转换及实战示例

    Halo 这里是Ppeua 平时主要更新C 数据结构算法 感兴趣就关注我吧 你定不会失望 文章目录 0 ROS中的坐标转换消息包 0 1 geometry msgs TransformStamped 0 2 geometry msgs Po
  • 【ZYNQ-GPIO MIO】Xilinx 知识点笔记(GPIO篇、MIO)

    1 GPIO是一个外设 用来对器件的引脚作观测 input 以及控制 output 2 MIO Multiuse I O 将来自PS外设和静态存储器接口的访问多路复用到PS的引脚上 3 GPIO可以独立且动态地编程 作为输入 输出以及中断模
  • 【数据结构】链表的实现以及应用

    目录 1 链表 1 1概念及结构 1 2链表的实现 1 2 1接口 1 2 2创建链表 1 2 3创建新的节点 1 2 4头插 1 2 5尾插 1 2 6头删 1 2 7尾删 1 2 8查询 1 2 9在pos指针之前插入数据 1 2 10