【数据结构入门精讲 | 第三篇】一文讲清表

在 RAP 里把编号这件事做得既优雅又合规:Early Numbering 与 Late Numbering 的落地指南 本文探讨了 SAP RAP 框架中的三种主键编号策略及其适用场景。文章首先指出传统 ABAP 应用中主键处理的局限性,详细分析了早期编号(外部/内部)和延迟编号的差异,强调延迟编号在避免业务断号方面的优势。通过 Partner 业务对象案例,文章演示了延迟编号的实现方法,包括行为定义配置、保存类实现和编号分配逻辑。同时指出测试中常见的校验陷阱,并提醒生产环境应避免简单的 MAX+1 方案,建议采用原子取号机制确保并发安全。全文从业务合规性和技术实现两个维度,为 RAP 应用的编号策略选择提供了实用指导。 阅读详情

在上一篇文章中我们进行了数据结构概念的练习和复杂度的计算,在这篇文章中我们将介绍表的相关知识点。

在这里插入图片描述

基础概念

表的定义:n(n>=0)个同一类型的元素组成的有限序列,如a(0),a(1),a(2),…,a(k),a(k+1),…,a(n)

本文将表分为顺序表和链表进行阐述。

顺序表(顺序存储)

SequenceList(顺序表)是一种数据结构,用于存储线性表的连续元素序列。它是在内存中分配一块连续的存储空间,通过数组的方式来表示线性表中的元素。顺序表中的元素在内存中是按照顺序依次存放的。

顺序表的特点是可以通过下标随机访问元素,因为元素在内存中是连续存储的。这使得顺序表在查找、访问特定位置的元素时具有高效性能。

用数组实现表时,进行插入运算所需的平均时间为O(n);进行删除运算所需的平均时间为O(n),所以顺序表适用于对元素的访问和查找操作较多的场景,但插入和删除操作相对较慢,因为需要移动其他元素。

链表(链式存储)

单链表

单链表是由指针实现的,用指针将存储表元素的单元依次串连在一起,就得到了链表。

基本思想如下:
用一组任意的存储单元存储线性表的数据元素 、每个表元素单元中设置指针表示表中元素之间的逻辑关系 、每个单元包括一个元素(数据域)和一个指针(指针域),其中指针指向表中下一个元素所在的单元
注意:尾部元素的指针是空指针

  • 在单链表上效率更高的操作
    • 删除所有值为x的数 O(1)
    • 删除某个任意元素 O(1)
    • 在任意元素后插入一个数 O(1)
  • 在顺序表上效率更高的操作
    • 在最后一个元素后面插入一个数
    • 交换任意两个元素的值
    • 取出某个元素的值

伪代码

函数ListInit()功能:创建一个空表

List ListInit()
{
    List L = malloc(sizeof *L);
    L->first = 0;
    return L;
}

ListEmpty(L)功能:判断表首指针first是否为空指针

int ListEmpty(List L)
{
    return L->first==0;
}

ListLength(L)功能:对表进行线性扫描计算表的长度

int ListLength(List L)
{
    int len = 0;
    link p = L->first;
    while (p)
    {
        /* 自表首指针始对表L进行线性扫描计算非空指针数目即为表长 */
        len++;
        p = p->next;
    }
    return len;
} // O(n)

ListRetrieve(k,L)功能:从表首开始向后线性扫描每一个元素直到找到第k个元素

ListItem ListRetrieve(int k, List L)
{
    int i;
    link p;
    if (k < 1) {
        Error("out of bounds");
    }
    p = L->first;
    i = 1;
    while (i < k && p) {
        p = p->next;
        i++;
    }
    return p->element;
} // 若表中不存在第k个元素,返回空指针,而非错误信息
// O(n)

PrintList(L)功能:从表首元素开始线性扫描整个链表并输出每个元素

void PrintList(List L)
{
    link p;
    for (p = L->first; p; p = p->next)
    {
        ItemShow(p->element);
    }
} // O(n)

ListInsert(k,x,L)功能:插入元素x到第k个元素之后。

void ListInsert(int k, ListItem x, List L)
{
    link p, y;
    p = L->first;
    for (int i = 1; i < k && p; i++)
    {
        p = p->next;
    }
    // 自表首始找插入位置
    y = NewNode();
    y->element = x;
    if (k) // 在位置p处插入节点y
    {
        y->next = p->next;
        p->next = y;
    }
    else // 在表首插入需要特殊处理
    {
        y->next = L->first;
        L->first = y;
    }
} // O(k)

ListDelete(k,L)功能:删除第k个元素

ListItem ListDelete(int k, List L)
{ 
    link p, q; 
    ListItem x; 
    int i;
    if (k < 1 || !L->first) 
        Error("out of bounds");
    p = L->first;
    if (k == 1) 
    {
        L->first = p->next; // 删除表首元素需要特殊处理
    } 
    else 
    {
        q = L->first; //自表首始找表中第k-1个元素所在结点q
        for (i = 1; i < k - 1 && q; i++) 
        {
            q = q->next; 
        }
        p = q->next; //让p指向 第k个元素所在结点
        q->next = p->next; // 删除结点p
    }
    x = p->element; // 第k个元素存⼊x并释放结点p
    free(p);
    return x;
}//o(k)

下面介绍三个对链表的简单处理

删除链表倒数第n个节点
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* newhead=new ListNode(0);
        newhead->next=head;
        ListNode* fast=newhead;
        ListNode* slow=newhead;
        while(fast!=nullptr&&n--)   fast=fast->next;
        fast=fast->next;
        while(fast!=nullptr)
        {
            fast=fast->next;
            slow=slow->next;
        }
        slow->next=slow->next->next;
        return newhead->next;
    }
合并两个有序链表
//将两个有序链表合并为一个升序链表,在两个有序链表间加上next关系即可
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        if(list1==NULL&&list2==NULL) return NULL;
        ListNode* head=new ListNode(-1);
        ListNode* prev=head;
        while(list1!=NULL&&list2!=NULL)
        {
           if(list1->val>=list2->val)
           {
               prev->next=list2;
               list2=list2->next;
           }else
           {
               prev->next=list1;
               list1=list1->next;
           }
           prev=prev->next;
        }
        if(list1!=NULL) prev->next=list1;
        else prev->next=list2;

        return head->next;
    }
反转链表
    ListNode* reverseList(ListNode* head) {
        ListNode* pre=NULL;
        ListNode* tmp;
        ListNode* cur=head;
        while(cur)
        {
            tmp=cur->next;
            cur->next=pre;
            pre=cur;
            cur=tmp;
        }
        return pre;
    }

单循环链表

以上为单链表的讲解,现在让我们开始单循环链表的介绍。

由于单链表中最后一个元素所在的单元的指针为Null,当我们将其指向表首单元时,这个链表就变成了首尾相接的链表,即单循环链表,也就是说,从任意一个单元出发,可以找到任何其它的单元。

图例如下:

在这里插入图片描述

因此:含有尾指针单循环链表实现删除头节点和在尾节点后插入的操作效率最高。

伪代码

ListRetrieve(k,L) 功能:从表中第一个元素开始向后扫描直到找到表中第k个元素

ListItem ListRetrieve(int k, List L)
{
    int i = 1;
    link p;
    if (k < 1 || k > L->n)
        Error("out of bounds");
    /* Last指针指向表尾元素,必须先找到表首元素开始线性扫描 */
    p = L->last->next->next;
    // 若无头结点,该语句为 p=L->last->next;
    while (i < k)
    {
        p = p->next;
        i++;
    }
    return p->element;
}
// O(k)

ListLocate(x,L)功能:从表中第一个元素开始逐个向后进行线性扫描直到找到表中元素x,返回其位置

int ListLocate(ListItem x, List L)
{
    int i = 1;
    link p;
    /* Last指针指向表尾元素,必须先找到表首元素开始线性扫描 */
    p = L->last->next->next;
    L->last->next->element = x;
    // 查找元素x暂时置于头结点中
    while (p->element != x)
    {
        p = p->next;
        i++;
    } // 最坏情况是终⽌于头结点
    return ((p == L->last->next) ? 0 : i);
    // 若p为头结点,则表中无元素x
} // O(n)

ListInsert(k,x,L)运算功能:将元素x插入第k个元素之后

// 使用哨兵结点,无须特殊处理表首元素插入,但由于通过表尾指针维护一张单循环链表,因此需要额外处理表尾指针
void ListInsert(int k, ListItem x, List L)
{
    link p, y;
    int i;
    if (k < 0 || k > L->n)
        Error("out of bounds");
    p = L->last->next;
    for (i = 1; i <= k; i++)
        p = p->next;
    // 自表首头结点开始查找插入位置
    y = NewNode();
    y->element = x;
    y->next = p->next;
    p->next = y; // 在位置p处插⼊
    if (k == L->n)
        L->last = y; // 表尾做特殊处理
    L->n++;
} // O(k)

ListDelete(k,L)运算功能:删除表中第k个元素

// 使用哨兵结点,无须特殊处理表首元素插入,但由于通过表尾指针维护一张单循环链表,因此需要额外处理表尾指针
ListItem ListDelete(int k, List L)
{
    link p, q;
    ListItem x;
    int i;
    if (k < 1 || k > L->n)
        Error("out of bounds");
    q = L->last->next;
    for (i = 0; i < k - 1; i++)
        q = q->next;
    p = q->next; // 让p指向第k个元素所在结点
    q->next = p->next; // 删除结点p
    if (k == L->n)
        L->last = q;
    x = p->element; // 第k个元素存入x并释放结点p
    free(p);
    L->n--;
    return x;
} // O(k)

PrintList(L) 功能:从表首元素开始线性扫描整个链表并输出每个元素

void PrintList (List L)
{
 link p;
 for (p = L->last->next->next; p!=L->last->next; p = p->next)
 ItemShow(p->element);
}
//O(n)

双循环链表

双链表,即在链表的每个结点设置两个指针,一个指向后继结点,另一个指向前驱结点。
在这里插入图片描述

开发出双循环链表的原因在于:单链表中不能找到当前结点的前驱,而循环链表中找到其前驱的时间为O(n),双循环链表的出现解决了该问题。

  • 链表为空的标志:head->prior==head&&head->next=head
  • 实现删除尾节点和在尾节点后插入的操作效率最高

在O(1)时间确定任一元素的前驱和后继元素所在的结点

伪代码

ListInit() 功能:创建一个仅由表首哨兵结点组成的空双向循环链表

List ListInit()
{
    link y;
    List L = (List)malloc(sizeof(List));
    y = NewNode(); // 采用了头结点的改进方法
    y->left = y;
    y->right = y;
    L->header = y;
    L->n = 0;
    return L;
}

ListRetrieve(k,L)功能: 从表中第一个元素开始向后扫描直到找到表中第k个元素

ListItem ListRetrieve(int k, List L)
{
    int i = 1;
    link p;
    if (k < 1 || k > L->n)
        Error("out of bounds");
    if (k == L->n)
        return L->header->left->element;
    p = L->header->right; // 表首结点
    while (i < k)
    {
        p = p->right;
        i++;
    }
    return p->element;
} // O(k)

ListLocate(x,L)功能: 从表中第一个元素开始逐个向后进行线性扫描直到找到表中元素x,返回其位置

ListItem ListLocate(ListItem x, List L)
{
    int i = 1;
    link p;
    p = L->header->right;
    L->header->element = x;
    while (p->element != x)
    {
        p = p->right;
        i++;
    }
    return ((p == L->header) ? 0 : i);
} // O(n)

ListInsert(k,x,L)功能:将元素x插入第k个元素之后,注意此时需要修改向前和向后两个方向的指针


同理,ListDelete(k,L)运算也要修改向前和向后两个方向的指针。

void ListInsert(int k, ListItem x, List L)
{
    link p, y;
    int i;
    if (k < 0 || k > L->n)
        Error("out of bounds");
    p = L->header;
    for (i = 1; i <= k; i++)
        p = p->right; // 自表首头结点始找插入位置
    y = NewNode();
    y->element = x;
    y->left = p;
    y->right = p->right;
    p->right->left = y;
    p->right = y;
    L->n++;
}

PrintList()功能:从表首元素开始线性扫描整个链表并输出每个元素

void PrintList (List L)
{
 link p;
 for (p = L->header->right; p!=L->header; p = p->right)
 ItemShow(p->element);
}
//o(n)

数组与指针的结合

表长较为固定时适合用数组实现,但表元素字节数比较庞大时直接用数组实现时表元素移动所需时间较为费时,解决方法为间接寻址法。

间接寻址法

间接寻址法(Indirect addressing)是计算机程序中一种常见的寻址方式,用于访问内存或寄存器中存储的地址值所指向的数据。

在间接寻址法中,操作数本身并不直接包含要访问的数据,而是包含一个地址值,该地址值指向实际存储数据的位置。通过间接寻址,可以间接地访问存储在其他位置的数据。

假设我们有一个存储器(内存)和一个寄存器R。内存中存储了一组数据,每个数据占据一个单元,而每个单元都有一个唯一的地址。

示意图如下所示:

内存地址   存储数据
  100      Data1
  101      Data2
  102      Data3
  103      Data4
  ...      ...

寄存器R的值: 100

在这个示例中,寄存器R的值为100,它存储了一个地址值。通过间接寻址,我们可以访问地址100对应的数据Data1。具体步骤如下:

  1. 从寄存器R中读取地址值100。
  2. 使用地址值100,在内存中找到对应的数据Data1。
  3. 对Data1进行操作或使用。

伪代码

ListEmpty(L)
功能:测试表是否为空

int ListEmpty(List L)
{ return (L->n==0); }
//O(1) 

ListLength(L)
功能:返回表的长度

int ListLength(List L)
{ return L->n; }
//O(1)

ListRetrieve(k,x)
功能:直接读取table数组第k-1个数组单元中指针所
指向的元素

ListItem ListRetrieve(int k, List L)
{ /*判断k值是否合理,若不合理,返回错误信息*/
 if (k<1 || k>L->n) Error (“out of bounds”);
 //k的取值为1<=k<=n
 return *L->table[k-1];
}
//O(1)

ListLocate(x,L)

功能:在table数组中从前往后依次取出指针所指的表元素和x比较,直到找到一个与x相等的数据元素,则返回它在数组中的存储下标+1;或者查遍整个数组都没有找到与 x 相等的元素,返回0

int ListLocate (LISTItem x, List L)
{ int i;
 for (i=0; i< L->n; i++)
 if (*L->table[i]==x) return ++i; 
 return 0;
}
//假设x存在于第i个位置,则找到x需要比较i次。若x不存在于表L中,则需要进行n次比较。最坏情况下,时间性能为O(n)

ListInsert(k,x,L) 功能:在table数组中将n~k+1处的元素指针分别后移到n+1~k+2,为新的元素指针让出位置

void ListInsert (int k, ListItem x, List L) 
{ int i;
 if (k<0||k>L->n) Error (“out of bounds”);
 if (L->n==L->maxsize) Error (“out of memory”);
 for (i=L->n-1;i>=k;i--) 
 L->table[i+1]=L->table[i]; //指针后移
 L->table[k]=NewNode();
 *L->table[k]=x; 
 L->n++; 
} 

ListDelete(k,L)功能:在table数组中将k+1~n处的元素指针分别前移到k~n-1

ListItem ListDelete (int k, List L) 
{ int i; ListItem x;
 addr p;
 if (k<1||k>L->n) Error (“out of bounds”);
 p= L->table[k-1];
 x=*p;
 for (i=k;i<L->n;i>=k;i++) 
 L->table[i-1]=L->table[i]; //指针前移
 L->n--; 
 free(p); 
 return x; 
}

PrintList(L) 打印链表

 void PrintList(List L)
 {
 int i;
 for (i=0; i<L->n; i++)
 ItemShow(*L->table[i]);
 }

总结如下:

优点 
随机存取表中任一位置的元素(继承数组实现的优点) 
修改指针而非实际移动表中元素以修改表中元素之间的逻辑关系(在每个元素占用空间比较大的时候,优势比较明显)
(继承指针实现的优点) 

缺点 
仍然增加了额外的存储空间(继承指针实现的缺点) 
数组的大小仍然需要预先估计(继承数组实现的缺点) 

适用情况:表长较为固定,但表元素所需字节比较大,间接寻址下指针移动所需时间小于直接用数组实现时表元素移动所需时间

游标表示法

数组与指针结合的另一种方法是:表的游标表示法。游标指的是数组中指示数组单元地址的下标值。

该方法的基本思想:用游标来模拟指针的方法 、数组和指针相结合,这样只需要修改游标不需要移动表中元素,就可以完成插入和删除运算。

伪代码

ListLength(L) 功能:沿着游标线性扫描表,计算长度

int ListLength(List L)
{ int i,len=0;
 i=L->first; //当前表结点游标
 while(i!=-1){
 len++;
 i=L->s->node[i].next;
 }
 return len;
}

ListRetrieve(k,x) 功能:沿着游标线性扫描表,直至找到第k个元素

ListItem ListRetrieve(int k, List L)
{ int p,i=1;
 if (k<1) Error("out of bounds");
 p=L->first;
 while (i<k && p!=-1){
 p=L->s->node[p].next;
 i++;
 }
 return L->s->node[p].element;
 }

ListLocate(x,L) 功能:沿着游标线性扫描表,直至找到元素x,返回其位置

int ListLocate(ListItem x, List L)
{ int p,i=1;
 p=L->first;
 while (p!=-1&&L->s->node[p].element!=x){
 p=L->s->node[p].next;
 i++;
 }
 return ((p>=0)?i:0);
}
//o(n)

ListInsert(k,x,L) 功能:沿着游标线性扫描表,直至找到第k个元素,即找到插入位置

void LsitInsert(int k, ListItem x, List L)
{ int p, y,i;
 if (k < 0) Error("out of bounds");
 p = L->first; 
 for ( i= 1; i < k && p!=-1; i++) 
 p =L->s->node[p].next; //自表首开始找插⼊位置
 y =SpaceAllocate(L->s );
 L->s->node[y]. element = x;
 if (k)
 { L->s->node[y]. next = L->s->node[p]. next; 
 L->s->node[p]. next = y; } // 在位置p处插⼊
 else 
 { L->s->node[y]. next = L->first; 
 L->first = y; } //未使用哨兵结点,在表首插⼊需要特殊处理
}

ListDelete(k,L) 功能:沿着游标线性扫描表,直至找到第k-1个元素,即找到删除位置

将删除元素所在数组单元释放到可用数组空间S中备用(第二个可用空间表)

ListItem ListDelete(int k, List L)
{ int p, q; ListItem x; int i;
 if (k < 1 || ! L->first==-1) Error("out of bounds");
 p = L->first;
 if (k == 1) 
 L->first = L->s->node[p]. next; // 删除表首元素特殊处理
 else {
 q =p; //自表首始找表中第k-1个元素所在结点q
 for (i=1; i<k-1&&q!=-1; i++) q= L->s->node[q]. next; 
 p = L->s->node[q]. next; //让p指向 第k个元素所在结点
 L->s->node[q]. next = L->s->node[p]. next;} // 删除结点p
 x = L->s->node[p]. element; // 第k个元素存⼊x并释放结点p
 SpaceDeallocate(p,L->s);
 return x;
 }

PrintList() 功能:打印表

void PrintList(List L) 
{
 int p;
 for (p=L->first; p!=-1; p=L->s->node[p].next)
 ItemShow(L->s->node[p].element;
}

总结如下:

优点 
可实现多个同类的表共享同一片连续存储空间,给用户予资源
调济的自主权 

缺点 
需要向操作系统申请多大的连续存储空间取决于具体的应用,不容易把握 

适用情形 
存在在多个同类型的表,且相互间存储状态呈现此消彼长的情况 
如果按照表长的极值情况为每张表开辟空间,空间比较浪费
58.Sobel边缘检测算子 注:在梯度运算中,很可能会出现负的灰度值,我们不能再采用uint8类型作为像素的数据类型,而是采用了CV_16S或者CV_32F这种有负数范围的类型,运算结果里的负数也不能不处理,否则在显示的时候就会被截断为0,这样就会丢失边界信息。如下图所示,对中心像素Z5求取多个方向的灰度差分,对于Z5的8邻域,分为4个方向,即对角线和水平垂直4个方向对:(z1, z9) ,(z2, z8), (z3, z7), (z6, z4) 给定邻域方向梯度矢量g的幅度为:|g| = <像素灰度差分>/<相邻像素的距离>。 阅读详情

相关推荐

OTP/EEPROM 读取与处理

Camera OTP/EEPROM 读取与处理 文章目录Camera OTP/EEPROM 读取与处理1. OTP概念1.1 基本概念1.2 OTP存储类型1.3 OTP烧录信息类型1.4 OTP 作用2. 配置准备2.1 基本认识2.2 OTP/EEPROM配置3. OTP/EEPROM datasheet理解 以及 读取方式3.1 data sheet 内容3.2 一般OTP/EEPROM(e...

Understand-image 1万+

数据结构-表、树、图

也叫哈希表,把数据分散在列表中,依赖于数组下标访问的特性,数组的一种拓展。即映射思想,用键值对来保存信息,键(key)和值(value)间的映射法则叫散列函数(Hash函数),通过散列函数得到的值叫散列值(Hash值)。散列函数根据key生成一个对应的散列值(数组下标),将key的信息保存到该数组下标对应的内存空间。构造散列函数三个基本要求1.通过散列函数得到的散列值是一个非负整数,因为散列值是数组下标。

weixin_42679286的博客 613

快速谱峭度算法解析

最近一直在研究振动信号的最优解调带宽寻找方法,而快速谱峭度算法是最常见的一种,因此本篇文章准备对该算法做个深入解析,也相当于自己的学习记录了。本篇文章对快速谱峭度算法做了一个较为详细的介绍,实际上就是通过多组滤波器将信号分解到不同的频段,再计算每个频段的峭度,从而找到值最大的频段,进一步地我们也可以选取其他指标作为频段的选择,以便更好地找到振动信号中的故障信息。

qq1234534215的博客 2742

【数据结构——表】

顺序表和链表的逻辑结构都是线性结构,都属于线性表,但是二者的存储结构不同,顺序表采用顺序存储,顺序存储的优点就是可以随机存放,存储密度高,缺点就是需要大片连续空间的分配不方便,改变容量不方便,然而链表采用的是链式存储,链式存储的优点就是离散的小空间分配方便,改变容量方便,缺点就是不可以随机存放,并且由于分配的空间不仅需要存放数据元素,还需要有一定的空间来存放指针,所以存储密度低。由于采用不同的存储方式来实现,因此基本操作的实现也不同

weixin_64993156的博客 932

Windows 64 位系统下安装配置 DOSBox 及 debug.exe

该篇博客同样作为类似问题 “如何在 Windows 64 位系统下进入 debug” 的答案。 很多同学初看王爽的《汇编语言》,或者因为其它原因需要进入 debug 调试模式下学习汇编语言的时候,会发现进入不了,出现类似以下的错误提示。 这是因为他们使用的是 64 位的 Windows 系统(Win 7,Win 8 以及 Win 10 等),而 64 位 Windows 系统不再支持在命令提示符窗口下进入 debug 调试模式。那么对于使用 64 位 Windows 系统的用户来说,该怎样才能够顺利进入 d

加号减减号的博客 5万+

高通平台 5G RF调试总结

目录: 1.QRCT4的使用 2.RFC配置 3.5G CA 配置概括 4.RFPD 运行及错误分析 5.CA吞吐率问题分析

嘿v小米的博客 9679

02 数据结构 ——链表(静态/循环/双向/LINUX内核链表)

Linux内核链表是一种通用的双向循环链表,面对通用的问题,Linux内核链表的节点干脆不存储任何数据域,只有指针域,节点只负责串联起每个节点,不负责存储数据。目的:根据结构体中某个成员的地址,能够计算出所在结构体的首地址,从而用户在设计Linux内核链表的节点时,不需要一定放在在数据的首位成员,增加可用性。在双向链表的基础上,让最后一个节点的next指向头节点,让头节点的prev指向最后一个节点,构成了双向循环链表。静态链表的节点存储在一段连续内存中,通过节点中称为游标的一个正整数来访问后继节点。

weixin_65029285的博客 636

Kimodo 入门教程(一)英伟达开源人形机器人动捕数据集训练运动学动作扩散模型

Kimodo是一个基于700小时商用光学动作捕捉数据集训练的运动学动作扩散模型,能够生成高质量的人体和机器人3D动作。该模型支持通过文本提示和多种约束条件(如关键帧、末端执行器位置等)进行控制,并提供数字人类和类人机器人两种模型变体。安装方式包括软件包安装和源代码安装两种,需要配置HuggingFace令牌以访问文本编码器。推荐使用GPU加速,最低需要17GB显存。项目提供命令行生成和交互式演示两种使用方式,演示程序支持时间轴编辑和实时3D可视化。模型分为商用友好版和受限版,其中SMPL-X版本需要额外申请

2506_90492529的博客 1929

数据结构-表(顺序表、链表)定义和基本操作

数据结构-表(顺序表、链表)定义和基本操作

早安不安的博客 2297

精度与速度双突破:YOLOv8-Pose 在 RK3588 上实现高性能人体姿态分析部署

本教程从YOLOv8-pose模型的姿态识别训练,到RK3588的边缘量化部署,为你打造了一套“高精度+高实时性+低成本”的人体姿态分析解决方案。无论是健身动作纠正、智能安防行为检测,还是工业人员姿态监控,这套技术都能直接复用——你只需替换数据集与业务逻辑,即可快速落地专属的边缘AI姿态识别系统。如果你在实践中遇到任何卡点,欢迎随时交流。记住,人体姿态识别的价值在于“关键点准、推理快、部署易”,而这三者的平衡,正是你通过本教程能掌握的核心能力。

know_F0727的博客 434

STM32 stack_size和heap_size大小设置的意义

在使用STM32编程时,一般情况下我们不会关注堆栈空间的大小,因为在STM32的启动文件中,已经帮我们预先设置好了堆栈空间的大小。如下图所示的启动代码中,Stack栈的大小为:0x400(1024Byte),Heap堆的大小为:0x200(512Byte)。 1、若工程中使用的局部变量较多,定义的数据长度较大时,若不调整栈的空间大小,则会导致程序出现栈溢出,程序运行结果与预期的不符或程序跑飞。这时我们就需要手动的调整栈的大小。 2、当工程中使用了malloc动态分配内存空间时,这时分配.......

zxm8513优快云博客 3万+

数据结构学习---表

一、什么是数据结构 1、数据结构的起源 1968年,美国高德纳教授,《计算机程序设计的艺术》的第一卷《基本算法》的出版,开创了数据结构和算法的先河 数据结构是一门研究数据之间的关系和操作的学科,而非计算方法 数据结构+算法=程序 沃斯,凭借这个观点,获得了图灵奖,这句话展示了程序的本质 2、数据结构的基本概念 数据: 所有能输入到计算机中,能被程序处理的描述客观事物的符号 (变量、数组) 数据元素: 组成数据的,有一定意义的基本单位,也称为节点(结构体变量) 数据项: 有独

mingbo64的博客 746

RK35X8调试YT8531单网口千兆以太网PHY芯片,修复ping不通或者IP地址丢失的问题

RK35X8上调试YT8531以太网PHY

荻夜的博客 3976

【数据结构入门精讲 | 第十一篇】一文讲清树

树是一种非线性的数据结构,也是一种表示一对多关系的数据结构,它由若干个节点(Node)和连接这些节点的边(Edge)组成。树有很多应用,如用于实现文件系统、数据库索引和编译器等。下面是树的一些常见概念及其相关知识点:1.根节点(Root):树的最顶层节点,它没有父节点。2.叶子节点(Leaf):没有子节点的节点。3.父节点(Parent):如果一个节点有子节点,则该节点称为其子节点的父节点。4.子节点(Child):一个节点的直接后继节点称为其子节点。5.节点的度(Degree):一个节点拥有

等风来 4193

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

【数据结构入门精讲 | 第十七篇】一文讲清图及各类图算法

深度优先搜索(DFS)是一种用于遍历或搜索树、图等数据结构的基本算法。该算法从给定的起点开始,沿着一条路径直到达到最深的节点,然后再回溯到上一个节点,继续探索下一条路径,直到遍历完所有节点或者找到目标节点为止。具体步骤如下:标记起始节点为已访问。访问当前节点,并获取其所有邻居节点。 遍历所有邻居节点,如果该邻居节点未被访问过,则递归地对该邻居节点进行深度优先搜索。 重复步骤2和步骤3,直到所有能够到达的节点都被访问过。 DFS算法使用了递归或者栈的机制,在每

等风来 5161

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

【数据结构入门精讲 | 第七篇】一文讲清全部排序算法(1)

冒泡排序是一种简单的排序算法。它重复地比较相邻的两个元素,并将它们按照顺序交换,从而将最大(或最小)元素 “浮” 到数组的末尾。这个过程类似于气泡在水中上浮的过程,因而得名 “冒泡排序”。

等风来 3903

【数据结构入门精讲 | 第八篇】一文讲清全部排序算法(2)

冒泡排序、起泡排序O(n^2)比较次数:n*(n-1)/2稳定空间O(1)插入排序最优(有序)O(n)最坏(逆序)O(n^2)稳定空间O(1)希尔排序不稳定空间O(1)快速排序平均、最好O(nlong)最坏O(n^2)不稳定空间O(logn)桶排序O(n)稳定空间O(n)归并排序O(nlogn)归并趟数的数量级:O(logn)稳定空间O(n)堆排序O(nlogn)不稳定空间O(1)选择排序O(n^2)比较次数:O(n^2)

等风来 4106

工程师的成长,不在于写了多少代码,而在于能否讲清楚代码背后的逻辑

他们会反复解释、不断分享,无论是在 Slack 对话里,技术文档中,还是 Zoom 会议里。很多人(尤其是刚入行时)误以为成长就是速度:更多的代码、更多的功能、更大的 PR。你试图向别人解释自己写的代码,结果说着说着,突然意识到自己其实并没完全搞懂?代码你可以靠“感觉”去写,靠搜索、模仿、试错,拼拼凑凑也能跑起来。这时你会意识到,那些所谓的“进度”,其实像沙堆一样脆弱。

粉丝们务必加入微信粉丝群 893

【超全汇总】学习数据结构与算法,计算机基础知识,看这篇就够了

由于文章有点多,并且发的文章也不是一个系列一个系列发的,不过我的文章大部分都是围绕着 数据结构 + 算法 + 计算机网络 + 操作系统 + Linux + 数据库 这几个方面发的,为了方便大家阅读,我整理了一波。不过公众号可以说是不支持修改文章,因为我决定每两三个月就整理一次,会非常详细着按照类别整理出来哦,并且也给出了目录哦。大家记得多看看哦,好多文章都是面试中常问滴 文章目录一、经验/经历/所...

帅地 10万+

终于有人把Python讲清楚了!!

从事Python开发的这些年中,我见过很多相关的教程和书籍,他们大都这样讲 :先介绍 Python 的基本语法规则、list、dict、tuple 等数据结构,然后再介绍字符串处理和正则表...

优快云云计算 328

数据结构入门:从基础到顺序表,C语言必学核心知识点一次性讲透

初学C语言的朋友大概率会遇到这样的问题:靠着基本数据类型和简单数组,能完成一些小demo的编写,但面对超市库存管理、地图行程规划这类实际开发场景,却显得力不从心。究其根本

bucenggaibian的博客 200

完美世界游戏研发笔试十年复盘:C++/算法/数据结构考点精讲

游戏研发工程师的笔试考察重点往往不是花哨的框架,而是扎实的编程基本功与底层原理理解。从C++的虚函数机制、智能指针内存管理,到操作系统进程线程与网络协议差异,再到算法与数据结构如LRU Cache的手写实现,这些知识共同构成了游戏引擎和服务器开发的核心技术底座。理解这些概念的本质、原理及其在真实工程中的应用价值,是应对游戏公司技术考核的关键。对于准备校招或转行的开发者而言,围绕C++、数据结构、操作系统、计算机网络和图形学基础进行系统复习,并重视代码手写能力与边界条件处理,能够有效提升笔试通过率,也为后续的

weixin_29001655的博客 277

终于有人把Python讲清楚了!

经常有人问我,Python初学者该怎么学好Python?其实从事Python开发的这些年中,我见过很多相关的教程和书籍,他们大都这样讲 :先介绍 Python 的基本语法规则、list、d...

AI科技大本营 364
上一篇: 【数据结构入门精讲 | 第一篇】打开数据结构之门
下一篇: 【数据结构入门精讲 | 第六篇】队列知识点及考研408、企业面试练习
秋说
秋说 优质创作者: 编程框架、信息安全技术领域 优质创作者: 编程框架、信息安全技术领域
博客等级 码龄4年 3万+粉丝 1906原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋说

感谢打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值