【数据结构】单链表的C语言实现--万字详解介绍
📝个人主页:@Sherry的成长之路
🏠学习社区:Sherry的成长之路(个人社区)
📖专栏链接:数据结构
🎯长路漫漫浩浩,万事皆有期待
文章目录
- 1.链表
- 1.1 链表的概念及结构:
- 1.2 顺序表的缺陷
- 1.3 链表的意义:
- 2.链表的分类:
- 2.1 单向或双向:
- 2.2 带头或不带头:
- 2.3 循环或非循环:
- 3.单链表的实现:
- 3.1 工程文件:
- 3.2 接口设计
- 3.3 `重点`接口实现:
- ①.创建新节点:
- ②.打印单链表:
- ③.单链表尾插:
- ④.单链表头插:
- ⑤.单链表尾删:
- ⑥.单链表头删:
- ⑦.单链表查找:
- ⑧.单链表插入:
- Ⅰ、单链表前插:
- Ⅱ、单链表后插:
- ⑨.单链表删除:
- I、删除pos位置的节点
- II、删除pos位置之后的节点
- ⑩.单链表销毁:
- 4.链表实现全部源码:
- 4.1 SList.h:
- 4.2 SList.c:
- 4.3 test.c:
- 5.总结:
1.链表
1.1 链表的概念及结构:
链表(linked list)是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
1.2 顺序表的缺陷
在之前的博客中,我们对顺序表有了较深层次的认知,同时我们发现,顺序表在创建和使用时,存在一些问题
:
- 当空间不足时,顺序表需要进行增容,但由于顺序表本质为数组,数据在物理上连续存放,因此在扩容时要付出较大的空间代价。为避免频繁扩容,基本每次都扩容 2 倍,当扩容次数增多时,就很有可能会造成一定程度的空间浪费。
- 由于顺序表内数据连续存储,我们在向顺序表中位置插入或删除数据时,就需要挪动大量的数据,效率不高。
1.3 链表的意义:
人们针对顺序表的这些缺陷,设计出了链表这个概念来克服这些不足。
而链表克服这些缺陷的方法是,将每一个节点划分为数据与指针两部分,数据部分用于存储数据而指针部分用于指向下一节点。这样做的好处是通过指针指向下一节点,于是数据的物理存储就可以不必连续,于是无论是在扩容,还是在插入与删除数据时,都能快速、方便的实现,并且不会造成很大的空间浪费。
2.链表的分类:
2.1 单向或双向:
2.2 带头或不带头:
2.3 循环或非循环:
(注:虽然有很多种不同的链表结构,但是最常用的链表结构主要是两种:无头单向非循环链表与带头双向循环链表)
无头单向非循环链表
:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。
带头双向循环链表
:结构最复杂,一般用在单独存储数据。虽然它结构复杂,但在实际使用中使用代码实现后,会带来很多优势,实现反而更加简单。并且在我们的实际中所使用的链表数据结构,一般都是带头双向循环链表。
3.单链表的实现:
3.1 工程文件:
与顺序表的实现方式类似,使用三个文件进行代码书写:
SeqList.h:存放函数声明、包含其他头文件、定义宏。
SeqList.c:书写函数定义,书写函数实现。
test.c:书写程序整体执行逻辑。
其中,我们的接口实现主要研究的是函数实现文件 SeqList.c 中的内容。
单链表和顺序表的结构略有不同,单链表的主结构为节点。
一个节点由两部分组成:data
和next
。data
为我们存储数据的地方。而next
则为下一个节点的地址。
typedef struct SListNode
{int data;struct SListNode* next;
}SLTNode;
上面我们设计的是不带哨兵位的单链表。这种结构设计相对于带哨兵位的链表的缺点就是设计接口函数时需要考虑链表是否为空的情况。
哨兵位
也叫哨兵节点,哑节点。该节点并不存储任何数据,只是为了方便操作而引入这个节点。起一个站岗放哨的作用。所以形象的叫它哨兵位。如果一个链表有哨兵位,那么链表的第一个元素应该是链表第二个节点对应的元素。这时链表永不为空,这就可以避免边界问题的处理,简化代码与减少代码出错的可能性。
3.2 接口设计
void SLPrint(SLNode* SLHead); //打印单链表
SLNode* BuyListNode(SLDataType x); //申请新节点
void SLPushBack(SLNode** pphead, SLDataType x); //单链表尾插
void SLPushFront(SLNode** pphead, SLDataType x); //单链表头插
void SLPopBack(SLNode** pphead); //单链表尾删
void SLPopFront(SLNode** pphead); //单链表头删
void SLFind(SLNode* phead, SLDataType x); //单链表查找
void SLInsertFront(SLNode** pphead, SLNode* pos, SLDataType x); //单链表前插
void SLInsertAfter(SLNode* pos, SLDataType x); //单链表后插
void SLErase(SLNode** pphead, SLNode* pos); //单链表删除
void SLEraseAfter(SLNode** pphead, SLNode* pos);//单链表后删除
void SLDestory(SLNode** pphead); //单链表销毁
我们发现绝大多数接口的参数都为二级指针,这是为什么?
我们先了解一下平时单链表的测试用例:
void TestList()
{SLTNode* plist = NULL;
}
链表的一开始是空的。所以我们插入数据时,需要让plist
指向我们新节点。就相当于改变plist的指向。plist是一级指针,那么要改变plist就要传它的地址&plist
,为二级指针,所以也需要用二级指针来接收该参数。
就好比当我们要改变一个int
类型的变量时a,我们需要传它的地址&a,那么函数的形参就应该用int*
接收;对于指针也是这样,一个int*
的指针变量p,它也是变量,我们需要改变这个值,就应该传它的地址&p,那么函数参数就应该那int* *
接收。
而对于一些接口就不需要传二级指针,就拿打印
来说吧,因为我并不需要改变plist,我只需要通过结构体指针访问结构内的next成员,并迭代到下一个节点,然后打印出数据就可以,所以不需要传二级指针。
3.3 重点
接口实现:
这里是本文的重点,即 SeqList.c 文件中的接口具体实现:
①.创建新节点:
在各项操作前,需动态创建新节点,再对新创建的节点进行操作。
SLNode* BuyListNode(SLDataType x)
{SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));if (newnode == NULL){perror("newnode:>");exit(-1);}newnode->data = x;newnode->next = NULL;return newnode;
}
②.打印单链表:
执行操作前无需进行断言,原因是循环条件为空指针,则条件不满足并停止循环,不会出现死循环。
根据指针循环操作,打印数据后使指针指向节点内存放下一节点地址的指针 next。
void SListPrint(SLNode* SLHead)
{SLNode* cur = SLHead;while (cur != NULL){printf("%d->", cur->data);cur = cur->next;}printf("NULL\n");
}
③.单链表尾插:
- 如果此时头节点为空指针,说明此时单链表内没有节点,此时只需要将申请来的新节点作为头节点即可。
- 若头节点不为空,则应当首先找到尾节点,使尾结点指针指向申请来的新节点后,再使新节点指针指向空即可。
void SLPushBack(SLNode** pphead, SLDataType x)
{SLNode* newnode = BuyListNode(x);if (*pphead == NULL){*pphead = newnode;}else{//找到尾结点SLNode* tail = *pphead;while (tail->next != NULL){tail = tail->next;}tail->next = newnode;}
}
测试接口:
④.单链表头插:
头插实现非常简单,首先申请新节点,先使新节点的指针指向原来的头节点,再使链表头指向新节点即可。
这里不需要考虑空节点的原因是,就算链表内没有内容,即首节点为空,新节点指针指向空也没有问题(申请新节点时原本就指向空)。
void SLPushFront(SLNode** pphead, SLDataType x)
{SLNode* newnode = BuyListNode(x);newnode->next = *pphead;*pphead = newnode;
}
测试接口:
⑤.单链表尾删:
执行操作前需要进行非空判断,防止传入空指针。
- 若链表内只有一个节点,则直接释放并置空即可。
- 若有一个以上节点,则当执行下面三个步骤:
- 首先寻找尾节点,找到后将其释放并置空。
- 但在释放前,必须将倒数第二个节点的指针指向空,否则将在指向最后一个被释放的节点时,变成野指针。
- 单向链表无法回溯,于是我们应当定义另一个指针,在每次移动尾节点指针前保存当前节点的位置。
void SLPopBack(SLNode** pphead)
{//判断链表是否为空:if (*pphead == NULL){return;}//若只有一个节点,直接释放并置空即可:if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}//若有多个节点则执行多步尾删:else{SLNode* tail = *pphead;SLNode* prev = NULL;//找到尾节点:while (tail->next != NULL){prev = tail;tail = tail->next;}free(tail);tail = NULL;prev->next = NULL;}
}
测试接口:
⑥.单链表头删:
执行操作前判断指针非空,防止对空指针进行操作。
使链表头指向第二个节点,接着将第一个节点释放并置空即可。
void SLPopFront(SLNode** pphead)
{if (*pphead == NULL){return;}else{SLNode* next = (*pphead)->next;free(*pphead);*pphead = next;}
}
测试接口:
⑦.单链表查找:
采用遍历思想
,依次对每一个节点的数据进行判断,符合条件即进行打印,不符合条件则继续向后遍历,直至指向 NULL,并且因此无需进行非空判断。
void SLFind(SLNode* phead, SLDataType x)
{SLNode* cur = phead;int count = 1;while (cur){if (cur->data == x){printf("第%d个节点:%p -> %d\n", count++, cur, x);}cur = cur->next;}
}
测试接口:
⑧.单链表插入:
单链表在插入新节点时有两种插入方式,一种是在目标节点前方插入,另一种是在目标节点后方插入,因此需要分别进行实现:
Ⅰ、单链表前插:
前插有两种情况:
- 在首节点处插入,此时即相当于头插操作。
- 在非首节点前插入,此时首先申请新节点,接着遍历单链表,找到插入位置后,使新节点指向目标节点后插入其前方。
void SLInsertFront(SLNode** pphead, SLNode* pos, SLDataType x)
{//首先申请新节点SLNode* newnod = BuyListNode(x);//头插if (*pphead == pos){newnod->next = *pphead;*pphead = newnod;}else{//找到目标节点 pos 的前一个节点:SLNode* PostPrev = *pphead;while (PostPrev->next != pos){PostPrev = PostPrev->next;}//找到后插入新节点:PostPrev->next = newnod;newnod->next = pos;}
}
测试接口:
同下图
Ⅱ、单链表后插:
在前插操作时,我们需要遍历整个链表来查找插入位置,因此效率较为低下,于是我们通常使用后插的方式插入新节点。后插直接申请节点,插入目标位置后即可。
void SLInsertAfter(SLNode* pos, SLDataType x)
{SLNode* newnode = BuyListNode(x);newnode->next = pos->next;pos->next = newnode;
}
测试接口:
⑨.单链表删除:
I、删除pos位置的节点
分为两种情况进行处理:
- 一种情况是整个链表中只有一个节点,则删除节点相当于释放整个数组并置空。
- 另一种情况是含有一个以上节点,此时只需要让目标节点的前一个节点指针指向后一个节点,再释放目标节点并置空即可。
void SLErase(SLNode** pphead, SLNode* pos)
{//头删if (*pphead == pos){*pphead = pos->next;free(pos);pos = NULL;}else{ //找到目标节点 pos 的前一个节点:SLNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}
}
测试接口:
同下图
II、删除pos位置之后的节点
和在pos位置之前插入一样,我们发现删除pos位置的节点,也很麻烦。因为需要找pos的前一个位置,所以这种设计也比较麻烦。
所以有时我们会删除pos位置之后的节点,这样就很方便了。
既然要删除pos位置之后的节点,那么我就需要将pos
位置的节点pos位置的 next 的 next 链接起来。那么我们首先用next
拷贝记录一下pos->next
,然后,再将pos->next
赋值为next->next
,也就是pos往后的两个节点。最后释放next
位置的节点即可。
注意
:pos位置的下一个节点不能是尾结点后面的节点
// 删除指定pos位置后的一个节点
void SLEraseAfter(SLNode** pphead, SLNode* pos)
{assert(pos);assert(pos->next);// 删除的不能是尾结点后面的位置SLNode* next = pos->next;// 拷贝pos的下一个节点pos->next = next->next;// 将pos的next变为下一个节点的nextfree(next);// 释放之前pos的下一个节点next = NULL;
}
测试接口:
⑩.单链表销毁:
遍历整个单链表,将每一个节点都进行释放并置空即可。
注意
:记得把*pphead置空,防止野指针问题出现
void SListDestory(SLNode** pphead)
{if (*pphead == NULL){return;}SLNode* cur = *pphead;while (cur){SLNode* next = cur->next;free(cur);cur = next;}*pphead = NULL;
}
测试接口:
4.链表实现全部源码:
4.1 SList.h:
#pragma once#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<string.h>
typedef int SLDataType;//单链表节点结构:
typedef struct SListNode
{SLDataType data;struct SListNode* next;
}SLNode;void SLPrint(SLNode* SLHead); //打印单链表
SLNode* BuyListNode(SLDataType x); //申请新节点
void SLPushBack(SLNode** pphead, SLDataType x); //单链表尾插
void SLPushFront(SLNode** pphead, SLDataType x); //单链表头插
void SLPopBack(SLNode** pphead); //单链表尾删
void SLPopFront(SLNode** pphead); //单链表头删
void SLFind(SLNode* phead, SLDataType x); //单链表查找
void SLInsertFront(SLNode** pphead, SLNode* pos, SLDataType x); //单链表前插
void SLInsertAfter(SLNode* pos, SLDataType x); //单链表后插
void SLErase(SLNode** pphead, SLNode* pos); //单链表删除
void SLEraseAfter(SLNode** pphead, SLNode* pos);//单链表后删除
void SLDestory(SLNode** pphead); //单链表销毁
4.2 SList.c:
#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"
//打印单链表:
void SLPrint(SLNode* SLHead)
{SLNode* cur = SLHead;while (cur != NULL){printf("%d->", cur->data);cur = cur->next;}printf("NULL\n");
}//申请节点:
SLNode* BuyListNode(SLDataType x)
{SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));if (newnode == NULL){perror("newnode:>");exit(-1);}newnode->data = x;newnode->next = NULL;return newnode;
}//单链表尾插:
void SLPushBack(SLNode** pphead, SLDataType x)
{SLNode* newnode = BuyListNode(x);if (*pphead == NULL){*pphead = newnode;}else{//找到尾结点SLNode* tail = *pphead;while (tail->next != NULL){tail = tail->next;}tail->next = newnode;}
}//单链表头插:
void SLPushFront(SLNode** pphead, SLDataType x)
{SLNode* newnode = BuyListNode(x);newnode->next = *pphead;*pphead = newnode;
}//单链表尾删:
void SLPopBack(SLNode** pphead)
{//判断链表是否为空:if (*pphead == NULL){return;}//若只有一个节点,直接释放并置空即可:if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}//若有多个节点则执行多步尾删:else{SLNode* tail = *pphead;SLNode* prev = NULL;//找到尾节点:while (tail->next != NULL){prev = tail;tail = tail->next;}free(tail);tail = NULL;prev->next = NULL;}
}//单链表头删
void SLPopFront(SLNode** pphead)
{if (*pphead == NULL){return;}else{SLNode* next = (*pphead)->next;free(*pphead);*pphead = next;}
}//单链表查找
void SLFind(SLNode* phead, SLDataType x)
{SLNode* cur = phead;int count = 1;while (cur){if (cur->data == x){printf("第%d个节点:%p -> %d\n", count++, cur, x);}cur = cur->next;}
}//单链表前插:
void SLInsertFront(SLNode** pphead, SLNode* pos, SLDataType x)
{//首先申请新节点SLNode* newnod = BuyListNode(x);if (*pphead == pos){newnod->next = *pphead;*pphead = newnod;}else{//找到目标节点 pos 的前一个节点:SLNode* PostPrev = *pphead;while (PostPrev->next != pos){PostPrev = PostPrev->next;}//找到后插入新节点:PostPrev->next = newnod;newnod->next = pos;}
}//单链表后插:
void SLInsertAfter(SLNode* pos, SLDataType x)
{SLNode* newnode = BuyListNode(x);newnode->next = pos->next;pos->next = newnode;
}//单链表删除:
void SLErase(SLNode** pphead, SLNode* pos)
{if (*pphead == pos){*pphead = pos->next;free(pos);pos = NULL;}else{SLNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}
}
// 删除指定pos位置后的一个节点
void SLEraseAfter(SLNode** pphead, SLNode* pos)
{assert(pos);assert(pos->next);// 删除的不能是尾结点后面的位置SLNode* next = pos->next;// 拷贝pos的下一个节点pos->next = next->next;// 将pos的next变为下一个节点的nextfree(next);// 释放之前pos的下一个节点next = NULL;
}
//单链表销毁:
void SLDestory(SLNode** pphead)
{if (*pphead == NULL){return;}SLNode* cur = *pphead;while (cur){SLNode* next = cur->next;free(cur);cur = next;}*pphead = NULL;
}
4.3 test.c:
#define _CRT_SECURE_NO_WARNINGS 1 #include "SList.h"// 测试尾插、带节点的头插
void TestList1()
{SLNode* plist = NULL;//SLPushBack(&plist, 1);//SLPushBack(&plist, 2);//SLPushBack(&plist, 3);//SLPushBack(&plist, 4);//SLPrint(plist);//SLPushBack(&plist, 5);//SLPrint(plist);SLPushFront(&plist, 1);SLPushFront(&plist, 2);SLPushFront(&plist, 3);SLPushFront(&plist, 4);SLPrint(plist);SLPushFront(&plist, 5);SLPrint(plist);}// 测试头插、尾删
void TestList2()
{SLNode* plist = NULL;SLPushFront(&plist, 1);SLPushFront(&plist, 2);SLPushFront(&plist, 3);SLPushFront(&plist, 4);SLPushFront(&plist, 5);SLPrint(plist);SLPopBack(&plist);SLPopBack(&plist);SLPopBack(&plist);SLPopBack(&plist);SLPrint(plist);SLPopBack(&plist);SLPrint(plist);}// 测试头删
void TestList3()
{SLNode* plist = NULL;SLPushFront(&plist, 1);SLPushFront(&plist, 2);SLPushFront(&plist, 3);SLPushFront(&plist, 4);SLPushFront(&plist, 5);SLPrint(plist);SLPopFront(&plist);SLPrint(plist);SLPopFront(&plist);SLPrint(plist);SLPopFront(&plist);SLPrint(plist);SLPopFront(&plist);SLPrint(plist);SLPopFront(&plist);SLPrint(plist);}void TestList4()
{SLNode* plist = NULL;SLPushFront(&plist, 1);SLPushFront(&plist, 2);SLPushFront(&plist, 3);SLPushFront(&plist, 2);SLPushFront(&plist, 5);SLNode* pos = SLFind(plist, 2);int i = 1;while (pos){printf("第%d个pos节点:%p->%d\n", i++, pos, pos->data);pos = SLFind(pos->next, 2);}// 修改 3->30pos = SLFind(plist, 3);if (pos){pos->data = 30;}SLPrint(plist);
}void TestList5()
{SLNode* plist = NULL;SLPushFront(&plist, 1);SLPushFront(&plist, 2);SLPushFront(&plist, 3);SLPushFront(&plist, 2);SLPushFront(&plist, 5);// 3前面插入一个30SLNode* pos = SLFind(plist, 3);if (pos){SLInsertFront(&plist, pos, 30);}SLPrint(plist);// 5后面插入一个10pos = SLFind(plist, 5);if (pos){SLInsertAfter( pos, 10);}SLPrint(plist);
}
void TestList6()
{SLNode* plist = NULL;SLPushFront(&plist, 1);SLPushFront(&plist, 2);SLPushFront(&plist, 3);SLPushFront(&plist, 2);SLPushFront(&plist, 5);SLPrint(plist);// 删除3位置节点SLNode* pos = SLFind(plist, 3);if (pos){SLErase(&plist, pos);}SLPrint(plist);// 删除4位置之后节点pos = SLFind(plist, 5);if (pos){SLEraseAfter(&plist, pos);}SLPrint(plist);SLDestory(&plist);SLPrint(plist);}int main()
{//TestList1();//TestList2();//TestList3();//TestList4();//TestList5();TestList6();return 0;
}
5.总结:
今天我们认识并学习了单向无头链表的相关概念、结构与接口实现,并且针对每个常用的功能接口都进行了实现分解,并对各个接口的各项注意点都进行了强调说明,希望我的文章和讲解能对大家的学习提供一些帮助。
当然,本文仍有许多不足之处,欢迎各位小伙伴们随时私信交流、批评指正!我们下期见~
相关文章:
【数据结构】单链表的C语言实现--万字详解介绍
📝个人主页:Sherry的成长之路 🏠学习社区:Sherry的成长之路(个人社区) 📖专栏链接:数据结构 🎯长路漫漫浩浩,万事皆有期待 文章目录1.链表1.1 链表的概念…...
电子科技大学软件工程期末复习笔记(七):测试策略
目录 前言 重点一览 V模型 回归测试 单元测试 集成测试 重要概念 自顶向下的集成方法 自底向上的集成方法 SMOKE方法 系统测试 验收测试 α测试 β测试 本章小结 前言 本复习笔记基于王玉林老师的课堂PPT与复习大纲,供自己期末复习与学弟学妹参考用…...
逆向-还原代码之除法 (Interl 64)
除法和32位差不多,毕竟背后的数学公式是一样的。区别只是32位的乘法需要两个寄存器来存放大数相乘的结果,而64位的不需要,一个寄存器就能存下。所以在64位的环境下,多了右移32位这条指令,其他指令一样。 //code #incl…...
Python WebDriver自动化测试
Webdriver Selenium 是 ThroughtWorks 一个强大的基于浏览器的开源自动化测试工具,它通常用来编写 Web 应用的自动化测试。 Selenium 2,又名 WebDriver,它的主要新功能是集成了 Selenium 1.0 以及 WebDriver(WebDriver 曾经是…...
2023年微信小程序获取手机号授权登录注册详细教程,包含服务端教程
前言 小程序中有很多地方都会用到用户的手机号,比如登陆注册,填写收货地址等等。有了这个组件可以快速获取微信绑定手机号码,无须用户填写。网上大多数教程还是往年的,而微信官方的api已做了修改。本篇文章将使用最新的方法获取手…...
YOLOv8模型学习笔记
在前面的章节中博主学习了YOLOv5的相关知识,从YOLOv5的数据增强处理到模型设计,从正负样本匹配策略到LOSS设计,今天博主学习的是YOLOv8,同为ultralytics公司的产品,两者无论是思想层面还是具体的设计方面都有着异曲同工…...
Java SE知识点1
一、continue、break、和return的区别是什么? 在循环结构中,当循环条件不满足或者循环次数达到要求时,循环会正常结束。但是,有时候可能需要 在循环的过程中,当发生了某种条件之后 ,提前终止循环,这就需要用到下面几个关键词: 1. continue :指跳出当前的这一次循环,…...
华为OD机试模拟题 用 C++ 实现 - 端口合并(2023.Q1)
最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 最多获得的短信条数(2023.Q1)) 文章目录 最近更新的博客使用说明端口合并题目输入输出示例一输入输出说明示例二输入输出说明示例三输入输出说明...
C++ Primer Plus 第6版 读书笔记(3) 第3章 处理数据
目录 3.1 简单变量 3.1.1 变量名 *位与字节 3.1.4 无符号类型 3.1.7 C如何确定常量的类型 C是在 C 语言基础上开发的一种集面向对象编程、泛型编程和过程化编程于一体的编程语言,是C语言的超集。本书是根据2003年的ISO/ANSI C标准编写的,通过大量短…...
ArrayList源码解读
参数 //默认初始容量private static final int DEFAULT_CAPACITY 10;//空数组(用于空实例)private static final Object[] EMPTY_ELEMENTDATA {};//用于默认大小空实例的共享空数组private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA {};//保存数据的数组tra…...
python实战应用讲解-【语法高级篇】时间与日期(附python示例代码)
目录 保持时间、计划任务和启动程序 time 模块 time.time() 函数 time.sleep() 函数 Python3 日期和时间...
D. Moscow Gorillas(双指针 + 区间分析)
Problem - D - Codeforces 在冬天,莫斯科动物园的居民非常无聊,尤其是大猩猩。你决定娱乐他们,带了一个长度为n的排列p到动物园。长度为n的排列是由n个从1到n的不同整数以任意顺序组成的数组。例如,[2,3,1,5,4]是一个排列…...
华为OD机试题,用 Java 解【相同数字的积木游戏 1】问题
最近更新的博客 华为OD机试题,用 Java 解【停车场车辆统计】问题华为OD机试题,用 Java 解【字符串变换最小字符串】问题华为OD机试题,用 Java 解【计算最大乘积】问题华为OD机试题,用 Java 解【DNA 序列】问题华为OD机试 - 组成最大数(Java) | 机试题算法思路 【2023】使…...
Python实现GWO智能灰狼优化算法优化BP神经网络分类模型(BP神经网络分类算法)项目实战
说明:这是一个机器学习实战项目(附带数据代码文档视频讲解),如需数据代码文档视频讲解可以直接到文章最后获取。1.项目背景灰狼优化算法(GWO),由澳大利亚格里菲斯大学学者 Mirjalili 等人于2014年提出来的一种群智能优…...
无线蓝牙耳机哪个牌子好?2023质量好的无线蓝牙耳机推荐
近几年,随着蓝牙技术的不断进步,使用蓝牙耳机的人也越来越多。蓝牙耳机的出现,不仅能让我们摆脱线带来的约束,还能提升我们学习和工作的效率。最近看到很多人问,无线蓝牙耳机哪个牌子好?下面,我…...
Qt之QTableView自定义排序/过滤(QSortFilterProxyModel实现,含源码+注释)
一、效果示例图 1.1 自定义表格排序示例图 本文过滤条件为行索引取余2等于0时返回true,且从下图中可以看到,奇偶行是各自挨在一起的。 1.2 自定义表格过滤示例图 下图添加两列条件(当前数据大于当前列条件才返回true,且多个列…...
电商(强一致性系统)的场景设计
领域拆分:如何合理地拆分系统? 一般来说,强一致性的系统都会牵扯到“锁争抢”等技术点,有较大的性能瓶颈,而电商时常做秒杀活动,这对系统的要求更高。业内在对电商系统做改造时,通常会从三个方面…...
算法与数据结构(一)
一、时间复杂度 一个操作如果和样本的数据量没有关系,每次都是固定时间内完成的操作,叫做常数操作。 时间复杂度为一个算法流程中,常数操作数量的一个指标。常用O(读作big O)来表示。具体来说,这个算法流程中,发生了多…...
【Python】元组如何创建?
嗨害大家好鸭!我是小熊猫~ Python 元组 Python 的元组与列表类似, 不同之处在于元组的元素不能修改。 元组使用小括号,列表使用方括号。 元组创建很简单,只需要在括号中添加元素, 并使用逗号隔开即可。 如下实例…...
qt操作文件以及字符串转换
//从文件加载英文属性与中文属性对照表QFile file(":/propertyname.txt");if (file.open(QFile::ReadOnly)) {//QTextStream方法读取速度至少快百分之30#if 0while(!file.atEnd()) {QString line file.readLine();appendName(line);}#elseQTextStream in(&file)…...
数组中只出现一次的两个数字(异或法思路)
题目简介 一个数组中只有2个数字只有一个,其他数字都有两个。找出这两个数字。a, b 用HashMap记录就不说了。 这里记录一下用异或的方式解决。 由于异或特性为自己异或自己为0。a^a 0;所以可以异或数组中的所有数字得出 a^b 的结果,其他相同的都消掉…...
python支持的操作系统有哪些
支持python开发环境的系统有Linux、OSX和windows,以及所有主要的操作系统中。 Linux,Linux系统是为编程而设计的,因此在大多数Linux计算机中,都默认安装了Python。编写和维护Linux的人认为会使用这种系统进行编程。要在Linux中运…...
S3C2440开发环境搭建
拿出了之前的S3C2440开发板,然后把移植uboot、移植内核、制作根文件系统、设备树编写驱动等几项再做一遍,这篇文章先记录下环境搭建过程,以及先把现成的uboot、内核、根文件系统下载进去,看看开发板还能不能用,先熟悉一…...
软件测试之测试用例
测试用例 1. 测试用例定义 测试用例又叫做test case,是为某个特殊目标而编制的一组测试输入、执行条件以及预期结果,以便测试某个程序路径或核实是否满足某个特定需求。 2. 编写测试用例的原因 2.1 理清思路,避免遗漏 如果测试的项目大而复杂&#…...
null和undefined的区别有哪些?
null和undefined的区别有哪些?相同点不同点undefinednull总结相同点 1.null和undefined都是js的基本数据类型 2.undefined和null都是假值(falsy),都能作为条件进行判断,所以在绝大多数情况下两者在使用上没有区别 if(undefined)…...
【强烈建议收藏:计算机网络面试专题:HTTP协议、HTTP请求报文和响应报文、HTTP请求报文常用字段、HTTP请求方法、HTTP响应码】
一.知识回顾 之前我们一起学习了HTTP1.0、HTTP1.1、HTTP2.0协议之前的区别、以及URL地址栏中输入网址到页面展示的全过程&&DNS域名解析的过程、HTTP协议基本概念以及通信过程、HTTPS基本概念、SSL加密原理、通信过程、中间人攻击问题、HTTP协议和HTTPS协议区别。接下来…...
关于Java中的静态块讲解
文章目录类的加载特性与时机类加载的特性类加载的时机static的三个常用地方什么是静态块?特点写法静态块 static怎么用?类的加载特性与时机 在介绍static之前可以先看看类的相关 类加载的特性 在JVM的生命周期里,每个类只会被加载一次。 类加载的原则…...
ledcode【用队列实现栈】
目录 题目描述: 解析题目 代码解析 1.封装一个队列 1.2封装带两个队列的结构体 1.3封装指向队列的结构体 1.4入栈函数实现 1.5出栈函数实现 1.6取栈顶数据 1.7判空函数实现 题目描述: 解析题目 这个题我是用c语言写的,所以队列的pu…...
【基础算法】双指针----字符串删减
🌹作者:云小逸 📝个人主页:云小逸的主页 📝Github:云小逸的Github 🤟motto:要敢于一个人默默的面对自己,强大自己才是核心。不要等到什么都没有了,才下定决心去做。种一颗树,最好的时间是十年前…...
Billu靶场黑盒盲打——思路和详解
一、信息收集 1、探测内网主机IP可以使用各种扫描工具比如nmap,我这里用的是自己编写的。 nmap -n 192.168.12.0/24 #扫描IP,发现目标主机 2、先不着急,先收集一波它的端口(无果) nmap -n 192.168.12.136 -p 1-10000…...
小程序获取wordpress背景音乐/免费自己建网站
2014文都英语:http://pan.baidu.com/s/1i3va0ZF 密码:1eh82014五大机构全程:http://pan.baidu.com/s/1dDGMgnv 密码:pcde2014年新东方范猛阅读:http://pan.baidu.com/s/1pJDjfpp2014海文课程全程班:http://…...
网站数据分析视频/seo排名点击软件运营
Day0 到了广州的瑞季悦居酒店,酒店设施显然不如从前(谁说之前的酒店烂的来着?),而且位置极为偏僻。 LJF甚至疑惑这家酒店设立在这里究竟是为了什么样的经济效益。 拼房时被人背叛,于是在高一中被挤出来。跟…...
wordpress 菜单间隔/代发推广百度首页包收录
1.将字符串的时间转换为时间戳方法:a "2013-10-10 23:40:00"#将其转换为时间数组import timetimeArray time.strptime(a, "%Y-%m-%d %H:%M:%S")#转换为时间戳:timeStamp int(time.mktime(timeArray))timeStamp 13814196002.格式更改如a "2013-10…...
惠州最专业的网站建设公司/北京网站优化体验
给 iOS 应用添加推送功能是一件比较麻烦的事情,本篇文章收集了集成 jpush-react-native 的常见问题,目的是为了帮助用户更好的排查问题 1. 收不到推送 确保是在真机上测试,而不是在模拟器自己的应用已经在 Apple developer 给应用配置推送功能…...
公司制作网站怎么做的/郑州网络营销推广机构
Closing期末结算【02年结流程】 (2016-11-02 23:05:51) 转载▼ 标签: 年结 ajab f.07 f.16 2kes 分类: FI财务会计【原创】 (加作者微信索取无水印PDF完整版) 在会计年度结束时需要将总账科目、供应商&客户、固定资产价值的年末余额结转…...
wordpress编辑主页/网站申请流程
非常简单的发送邮件实现,网上有很多啦,但还是自己写写记录下吧。 package cn.jmail.test;import java.util.Properties;import javax.mail.*; import javax.mail.internet.*;public class FirstMail {/*** 发送简单邮件方法* param host 发送邮件服务…...