linux:数据结构

 一、数据结构

1、数据结构的概念

数据结构:相互之间存在一种或多种特定关系的数据元素的集合。

   
    逻辑结构
        集合,所有数据在同一个集合中,关系平等。
        线性,数据和数据之间是一对一的关系
        树, 一对多
        图,多对多
        
    物理结构(在内存当中的存储关系)
        顺序存储,数据存放在连续的存储单位中。逻辑关系和物理关系一致
        链式,数据存放的存储单位是随机或任意的,可以连续也可以不连续。

2、数据的概念

    struct Per 数据元素
    {
        char name;//数据项
        int age;
        char phone;
    }    
            
    struct Per list[100]; //数据对象
        
    数据的类型,ADT    abstruct datatype 
        是指一组性质相同的值的集合及定义在此集合上的一些操作的总称。
        原子类型,int,char,float
        结构类型,sturct, union,

        抽象数据类型, 数学模型 + 操作。

3、算法

          程序 =  数据 + 算法
    
算法,
    是解决特定问题求解步骤的描述,计算机中表现为指令的有限序列,每条指令表示一个或多个操作。    
    算法的特征,
    1,输入,输出特性,输入时可选的,输出时必须的。
    2,有穷性,执行的步骤会自动结束,不能是死循环,并且每一步是在可以接受的时间内完成。
    3,确定性,同一个输入,会得到唯一的输出。
    4,可行性,每一个步骤都是可以实现的。
        
    算法的设计,
    1,正确性,
        语法正确
        合法的输入能得到合理的结果。
        对非法的输入,给出满足要求的规格说明
        对精心选择,甚至刁难的测试都能正常运行,结果正确
    2,可读性,便于交流,阅读,理解
    3,健壮性,输入非法数据,能进行相应的处理,而不是产生异常
    4,高效,存储低,效率高 


算法时间复杂度
    也就是执行这个算法所花时间的度量
    n  1  = O(n)   O(1)
    推到时间复杂度
        1,用常数1 取代运行时间中的所有加法常数
        2,在修改后的运行函数中,只保留最高阶项。
        3,如果最高阶存在且不是1,则取除这个项相乘的常数

常见的时间复杂度:Ο(1)<O(log_{2}^{n})<O(n)<O(nlog_{2}^{n})<O(n^{2})<O(2^{n})<O(n!)< O(nn) (常对幂指阶)

空间复杂度:算法在执行过程中需要的辅助空间数量, 递归程序看递归深度与问题规模n的关系

二、线性表

线性表
    零个或多个数据元素的有限序列
    元素之间是有顺序了。如果存在多个元素,第一个元素无前驱,最有一个没有后继,其他的元素只有一个前驱和一个后继。
    当线性表元素的个数n(n>=0)定义为线性表的长度,当n=0时,为空表。在非空的表中每个元素都有一个确定的位置,如果a1是第一个元素,那么an就是第n个元素。

1、顺序表

线性表顺序存储的优点,缺点
优点
    1,无需为表中的逻辑关系增加额外的存储空间
    2,可以快速随机访问元素O(1)
缺点
    1,插入,删除元素需要移动元素o(n)
    2,无法动态存储。

2、链表

线性表的链式存储
    解决顺序存储的缺点,插入和删除,动态存储问题。
特点:
    ,线性表链式存储结构的特点是一组任意的存储单位存储线性表的数据元素,存储单元可以是连续的,也可以不连续。可以被存储在任意内存未被占用的位置上。
    
    所以前面的顺序表只需要存储数据元素信息就可以了。在链式结构中还需要一个元素存储下一个元素的地址。
    
    为了表示每个数据元素,ai与其直接后继数据元素ai+1之间的逻辑关系,对ai来说,除了存储其本身的信息外,还需要存一个指示器直接后续的信息。把存储元素信息的域叫数据域,把存储直接后继位置的域叫指针域。这两部分信息组成数据元素ai的存储映像,叫结点(Node);
 

顺序表和链表 优缺点
    存储方式:
        顺序表是一段连续的存储单元
        链表是逻辑结构连续物理结构(在内存中的表现形式)不连续
    时间性能,
        查找 顺序表O(1)
             链表  O(n)
        插入和删除
            顺序表 O(n)
            链表   O(1)
            
    空间性能
            顺序表 需要预先分配空间,大小固定
            链表, 不需要预先分配,大小可变,动态分配
            
            
    循环链表
        简单的来说,就是将原来单链表中最有一个元素的next指针指向第一个元素或头结点,链表就成了一个环,头尾相连,就成了循环链表。circultlar linker list.
        
        注意非空表,和空表。多数会加入头结点。
        原来结束的条件是
        p->next != NULL ------->>>>> p-next != Head

3、单向链表

4、双向链表

练习:双向链表的逆序

    LinkNode *prev = NULL;
    LinkNode *tmp = list->head;
    LinkNode *next = NULL;
    while (tmp != NULL) {
        next = tmp->next; 
        tmp->next = prev; 
        tmp->prev = next; 
        prev = tmp; 
        tmp = next; 
    }
    list->head = prev; 

三、栈

栈的定义

栈(Stack)是一种常见的数据结构,它是一种“后进先出”(Last In First Out,LIFO)的数据结构。栈可以看做是一种特殊的线性表,只能在栈顶进行插入和删除操作。栈顶是允许操作的,而栈底是固定的。

1、顺序栈的基本概念 


顺序栈是一种使用数组实现的栈,也称为数组栈。其基本思路是通过数组来存储栈中的元素,并通过栈顶指针指示栈顶元素在数组中的位置。顺序栈具有以下特点:

存储结构:使用数组作为底层存储结构,数组的每个元素存储栈中的一个元素;
操作受限:栈只能从栈顶插入和删除元素,不支持在栈中间插入和删除元素;
先进后出:栈的元素遵循“先进后出”(Last In First Out, LIFO)的原则,即后插入的元素先被删除;
顺序访问:只能从栈顶开始访问栈中的元素,不能从栈底或中间位置访问元素。
顺序栈的实现非常简单,可以使用数组和栈顶指针两个变量来实现。顺序栈的主要操作包括初始化、入栈、出栈、获取栈顶元素、判断栈是否为空以及获取栈中元素的数量等。由于顺序栈的存储结构是数组,因此在使用过程中需要考虑数组大小的限制,当栈中元素数量超过数组大小时,需要对数组进行扩容。

注意:除了遍历栈中的元素的操作时间复杂度为O(n)外,其余:入栈、出栈、取栈顶元素、判断栈是否为空操作的时间复杂度均为O(1)。

栈顶(Top):线性表允许进行插入删除的那一端。
栈底(Bottom):固定的,不允许进行插入和删除的另一端。
空栈:不含任何元素的空表。

2、顺序栈的基本操作

头文件:

#ifndef __SEQSTACK__H__
#define __SEQSTACK__H__


typedef struct	person {
    char name[32];
    char gender;
    int age;
    int score;
}DATATYPE;

typedef struct {
    DATATYPE *head;
    int tlen;
    int top;//clen
}SeqStack;


SeqStack* CreateSeqStack(int len);
int DestroySeqStack(SeqStack* ss);
int PushSeqStack(SeqStack* ss, DATATYPE*data);//压栈 入栈 插入栈
int PopSeqStack(SeqStack*ss);//出栈 弹栈 删除
int IsEmptySeqStack(SeqStack*ss);
int IsFullSeqStack(SeqStack*ss);
DATATYPE* GetTopSeqStack(SeqStack* ss);
int GetSizeSeqStack(SeqStack*ss);

#endif  //!__SEQSTACK__H__

核心代码:

#include "seqstack.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


SeqStack* CreateSeqStack(int len)
{
    SeqStack* ss = malloc(sizeof(SeqStack));       
    if(NULL == ss)
    {
        perror("create seq stack malloc");
        return NULL;
    }
    ss->head= malloc(sizeof(DATATYPE)*len);
    if(NULL == ss->head)
    {
        perror("create seq stack malloc2");
        return NULL;


    }
    ss->tlen= len;
    ss->top = 0;
    return ss;
}
int DestroySeqStack(SeqStack* ss);
int PushSeqStack(SeqStack* ss, DATATYPE*data)
{
    if(IsFullSeqStack(ss))
    {
        return 1;
    }
    memcpy(&ss->head[ss->top++],data,sizeof(DATATYPE));
    return 0;
}
int PopSeqStack(SeqStack*ss)
{
    if(IsEmptySeqStack(ss))
    {
        return 1;
    }
    ss->top--;
    return 0;
}
int IsEmptySeqStack(SeqStack*ss)
{
    return ss->top ==  0;   
}
int IsFullSeqStack(SeqStack*ss)
{
    return ss->tlen == ss->top;
}
DATATYPE* GetTopSeqStack(SeqStack* ss)
{
    return &ss->head[ss->top-1];
}

int GetSizeSeqStack(SeqStack*ss)
{
    return ss->top;
}

 3、栈的类型

 

 4、链栈

 链栈的基本概念

链栈是一种基于链表实现的栈,其特点是无需事先分配固定长度的存储空间,栈的长度可以动态增长或缩小,避免了顺序栈可能存在的空间浪费和存储溢出问题。

链栈中的每个元素称为“节点”,每个节点包括两个部分:数据域和指针域。数据域用来存储栈中的元素值,指针域用来指向栈顶元素所在的节点。

链栈的基本操作包括入栈、出栈、获取栈顶元素和遍历等,相比顺序栈而言,链栈的实现难度稍高,但其在某些情况下有着更好的灵活性和效率,特别适用于在动态存储空间较为紧缺的场合。

链栈的进栈push和出栈pop操作都很简单,时间复杂度均为O(1)

注意:如果栈的使用过程中元素变化不可预料,那么最好使用链栈,反之,如果它的变化在可控范围内,建议使用顺序栈。

 5、链栈的基本操作

头文件

#ifndef _LINK_STACK_ 
#define _LINK_STACK_ 

typedef struct	person {
    char name[32];
    char gender;
    int age;
    int score;
}DATATYPE;
 

typedef struct _link_stack_node_
{
    DATATYPE data;
    struct _link_stack_node_* next;
}LinkStackNode;

typedef struct 
{
    LinkStackNode* top;
    int clen;

}LinkStack;
LinkStack* CreateLinkStack();
int DestroyLinkStack(LinkStack*ls);
int PushLinkStack(LinkStack*ls ,DATATYPE*data);
int PopLinkStack(LinkStack*ls);
DATATYPE* GetTopLinkStack(LinkStack*ls);
int IsEmptyLinkStack(LinkStack*ls);
int GetSizeLinkStack(LinkStack* ls);
#endif

核心代码

#include "linkstack.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
LinkStack* CreateLinkStack()
{
   LinkStack* ls= (LinkStack*) malloc(sizeof(LinkStack));
   if(NULL == ls)
   {
    perror("CreateLinkStack malloc");
    return NULL;
   }
   ls->top = NULL;
   ls->clen = 0 ;
   return ls;
}
int DestroyLinkStack(LinkStack*ls)
{
    int len = GetSizeLinkStack(ls);
    int i = 0 ;
    for(i=0;i<len;i++)
    {
        PopLinkStack(ls);
    }

    free(ls);
    return 0;
}
int PushLinkStack(LinkStack*ls ,DATATYPE*data)
{
    LinkStackNode*newnode =  (LinkStackNode*)malloc(sizeof(LinkStackNode));
    if(NULL == newnode)
   {
    perror("push malloc");
    return 1;
   }
    memcpy(&newnode->data,data,sizeof(DATATYPE));
    newnode->next = NULL;

    newnode->next= ls->top;
    ls->top = newnode;
    ls->clen++;
    return 0;
}
int PopLinkStack(LinkStack*ls)
{

    if(IsEmptyLinkStack(ls))
    {
        return 1;
    }

    LinkStackNode* tmp = ls->top;
    ls->top=ls->top->next;
    free(tmp);

    ls->clen--;
    return 0;
}
DATATYPE* GetTopLinkStack(LinkStack*ls)
{
    if(IsEmptyLinkStack(ls))
    {
        return NULL;
    }
    else 
    {
        return &ls->top->data ;
    }

}
int IsEmptyLinkStack(LinkStack*ls)
{
    return 0 == ls->clen ;
}
int GetSizeLinkStack(LinkStack* ls)
{
    return ls->clen;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值