链表和顺序表

文章目录

链表

1.链表就是一种数据结构,可以从上一个数据找到下一个数组的位置。从而进行访问。
一般标准的符合上述描述的就是单链表。‘
单链表示意图
在理解具体结结构的基础上,就可以着手链表的创建、销毁以及增删查改.
具体在于头插,尾插,头删,尾删,某个位置插入数据,查找数组,删除某位置数据

#include  <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLdata;

typedef struct SLCA {
    struct SLCA* p;
    SLdata a;
}CA;//取名叫CA,是因为查单词出现偏差,chain list,CA也就是链表的结构体
//里面包含了一个指针,指向下一个数据
void SL_firinsert(CA** pca,SLdata data);
void SL_endinsert(CA** pca,SLdata data);
void SLprint(CA* pca);
CA* Crea_node(SLdata X);
void SL_deletefir(CA** pca);
void SL_deletefend(CA** pca);
CA* SLdfind(CA* phead,SLdata data);
void SLindata(CA** phead,CA* pos,SLdata x);
void SLindata2(CA** phead,CA* pos,SLdata x);
void SLdele_now(CA** phead,CA* pos);
void SLdele_aft(CA** phead,CA* pos);

void SLprint(CA* pca)//打印链表,可以传空
{
    CA* cur = pca;
    while(cur)
    {
        printf("%d ->",cur->a);
        cur = cur->p;
    }
    printf("NULL\n");
    return ;
}

CA* Crea_node(SLdata X)//创建一个链表节点
{
    CA* newnode = (CA*)malloc(sizeof(CA));
    if(!newnode)
    {
        perror("malloc fail:");
        return NULL;
    }
    newnode->a = X;
    newnode->p = NULL;
    return newnode;
}
void SL_firinsert(CA** pca,SLdata data)//头插,这里传二级指针,是为了确实修改节点,
//如果直接传以及指针,是不是就无法修改你改变头节点之后的值的还回去
{
    assert(pca);
    CA* cur = *pca;
    CA* temp = Crea_node(data);
    temp ->a = data;
    temp->p = cur;
    *pca = temp;
    return;
}

void SL_endinsert(CA** pca,SLdata data)//尾插
//这里一样,如果是一开始传的空头,那么就需要二级指针,
{
    assert(pca);
    CA* temp = Crea_node(data);
    temp->a = data;
    temp->p=NULL;
    CA* p = *pca;
    if(p)
    {
        
        while(p->p)
        {
            p = p->p;
        }
        p->p = temp;
    }
    else
    {
        *pca = temp;
    }
    return ;
}




void SL_deletefir(CA** pca)
{
    assert(pca);
    if(*pca)//不为空,去删除
    {
        CA* cur = *pca;
        *pca = cur->p;
        free(cur);//free the place malloc;
    }
    else{
        perror("plist is NULL\n");
        return ;//已经为空
    }
}

void SL_deletefend(CA** pca)
{
    assert(pca);
    if(*pca)//不为空,去删除
    {
        if((*pca)->p)
        {
            CA* cur = *pca;
            while(cur->p->p)
            {
                cur = cur->p;
            }
            free(cur->p);//free the place malloc;
            cur->p = NULL;
        }
        else
        {
            free(*pca);
            *pca = NULL;
        }
    }
    else{
        perror("plist is NULL\n");
        return ;//已经为空
    }
}

CA* SLdfind(CA* phead,SLdata data)
{
    CA* cur = phead;
    while(cur)
    {
        if(cur->a== data)
        {
            return cur;
        }
        cur = cur->p;
    }
    printf("NO this DATA!\n");
    return NULL;
}

//某节点前插入
void SLindata(CA** phead,CA* pos,SLdata x)
{
    assert(phead);
    CA* cur = *phead;
    if(!(*phead))
    {
        *phead = Crea_node(x);
        return ;
    }
    if(!pos)
    {
        perror("pos is NULL\n");
        return;
    }
    if(*phead == pos)//solve teh probem if here only one value or it the head of the list insert
    {
        SL_firinsert(phead,x);
        return ;
    }
    while(cur && cur->p != pos)//if cur is NULL ,exit
    {
        cur = cur->p;
    }
    if(cur)
    {
        CA*newnode = Crea_node(x);
        newnode->p = pos;
        cur->p = newnode;
    }
    else
    {
        printf("NO pos int the list\n");
    }

}

void SLindata2(CA** phead,CA* pos,SLdata x)
{
    assert(phead);
    CA* cur = *phead;
    if(!(*phead))
    {
        *phead = Crea_node(x);
        return ;
    }
    if(!pos)
    {
        perror("pos is NULL\n");
        return;
    }
    while(cur && cur!=pos)
    {
        cur = cur->p;
    }
    if(cur)
    {
        CA*newnode = Crea_node(x);
        cur->p = newnode;
    }
    else
    {
        printf("NO pos int the list\n");
    }
}

void SLdele_aft(CA** phead,CA* pos)
{
    assert(phead);
    CA* cur = *phead;
    if(!(*phead))
    {
        printf("thee head is NULL!\n");
        return ;
    }
    while(cur && cur!=pos)
    {
        cur = cur->p;
    }
    if(!cur->p)
    {
        printf("the pos after the target is NULL.\n");
    }
    else
    {
        CA* t = pos->p;
        cur->p = cur->p->p;
        free(t);
    }
}


void SLdele_now(CA** phead,CA* pos)
{
    assert(phead);
    if(!pos)
    {
        perror("pos is NULL\n");
        return ;
    }
    CA* cur = *phead;
    if(!(*phead))
    {
        printf("thee head is NULL!\n");
        return ;
    }
    if(cur==pos)
    {
        *phead = cur->p;
        free(pos);
    }
    else
    {
        while(cur->p &&cur->p!=pos)
        {
            cur = cur->p;
        }
        if(cur->p)
        {
            cur->p = cur->p->p;
            free(pos);
        }
    }
}

详细需要注意的我标准在代码里面了。可以自己动手去实现下。牢记物理结构,结合C语言特征去尝试的调试代码即可。
链表特点:根据代码和物理结构,我们就知道,链表对数据头部的改动是非常方便的,但是对于随机访问和查找数据就不方便。
同时也不方便访问尾部数据。
这里其实有一种解决方式,就是双向链表。
有种很好用的就是带头双向链表。
如下图:
双向链表示意图

typedef int SLdata;
struct ListNode
{
    struct ListNode* next;
    struct ListNode* prev;
    SLdata val;
};
typedef struct ListNode LIST;
void init(LIST ** head);
void pushback(struct ListNode* head,SLdata x);
void pushfront(struct ListNode* head ,SLdata x);
void SLprint(struct ListNode* head);
void popback(struct ListNode* head);
void popfront(struct ListNode* head);
struct ListNode*SLfind(struct ListNode* head,SLdata x);
void intsert_befpos(struct ListNode* pos,SLdata x);
void intsert_aftpos(struct ListNode* pos,SLdata x);


struct ListNode* creat_node(SLdata x)
{
    struct ListNode* node = (struct ListNode*)malloc(sizeof(struct ListNode));
    node->prev=NULL;
    node->next=NULL;
    node->val=x;
    return node;
}
void init(LIST ** head)
{
    *head = (struct ListNode*)malloc(sizeof(struct ListNode));
    (*head)->next =(*head);
    (*head)->prev = (*head);
    (*head)->val = -1;
}
void pushback(struct ListNode* head,SLdata x)//尾插
{
    assert(head);
    LIST* cur = head;
    LIST* newnode = creat_node(x);
    cur = cur->prev;
    cur->next = newnode;
    newnode->next = head;
    newnode->prev = cur;
    head->prev = newnode;
}


void pushfront(struct ListNode* head,SLdata x)
{
    assert(head);
    LIST* cur = head->next;
    LIST* newnode = creat_node(x);
    cur->prev = newnode;
    newnode->next = cur;
    newnode->prev = head;
    head->next = newnode;
}
void SLprint(struct ListNode* head)
{
    assert(head);
    assert(head->next!=head);
    printf("guard-> ");
    struct ListNode* cur = head->prev;
    while (cur!=head)
    {
        printf("%d->",cur->val);
        cur = cur->prev;
    }
    printf("\n");
}
void popback(struct ListNode* head)
{
    assert(head);
    assert(head->next!=head);
    struct ListNode *cur = head->prev;
    struct ListNode * fir = cur->prev;
    fir->next = head;
    head->prev = fir;
    free(cur);
}
void popfront(struct ListNode* head)
{
    assert(head);
    assert(head->next!=head);
    struct ListNode *cur = head->next;
    struct ListNode * sec = cur->next;
    head->next = sec;
    sec->prev = head;
    free(cur);
}
struct ListNode* SLfind(struct ListNode* head,SLdata x)
{
    assert(head);
    assert(head->next!=head);
    struct ListNode *cur = head->next;
    while(head != cur)
    {
        if(cur->val==x)
        {
            return cur;
        }
        cur = cur->next;
    }
    return NULL;
}
void intsert_befpos(struct ListNode* pos,SLdata x)
{
    if(!pos)
    {
        perror("the pos is NULL\n");
        return ;
    }
    struct ListNode* pre = pos->prev;
    LIST* newnode = creat_node(x);
    pre->next = newnode;
    newnode->next = pos;
    newnode->prev = pre;
    pos->prev = newnode;
}


void intsert_aftpos(struct ListNode* pos,SLdata x)
{
    if(!pos)
    {
        perror("the pos is NULL\n");
        return ;
    }
    struct ListNode* sec = pos->next;
    LIST* newnode = creat_node(x);
    sec->prev = newnode;
    newnode->next = sec;
    newnode->prev = pos;
    pos->next = newnode;
}

这种链表使用起来就方便许多,在查找数据和增删查改都方便不少。

顺序表

顺序表其实就是我们常说的数组来实现的,只是一般数据都是结构体类型。
特点可以通过下标快速访问,对尾部数据操作时比较方便,还可以进行排序等操作,但是挪动头部数据就需要较大的时间复杂度,并且会需要相对较大的内存。

顺序表也是增删查改,几个操作,可以类似

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
enum SLelments{
	NAME = 20,
	AGE = 10,
	GRADE  =6
};

//定义一个学生结构体 
struct S{
	char name[NAME];
	int age;
	int* grade;//去动态内存开辟 
};

typedef int sldata;//这里暂时将int类型定义位本次顺序表的元素类型。
//测试时可以把上面的学生结构体进行替换即可。 

struct seplist{
	sldata* a;//元素类型 
	int sz;//这个数据表有效数据个数 
	int capacity;//这个顺序表容量 
};
typedef struct seplist SEL;
void cr_SL(SEL* p);//初始化创建一个顺序表 ,结构体传地址
void des_SL(SEL* p);
void dul_SL(SEL* p);

void c_data(sldata*  t,const sldata* tem);

void first_add(SEL*p,const sldata*); 
void end_add(SEL*p,const sldata*);//尾部加一个元素 
void firdelete(SEL*p);
void enddelete(SEL*p);
void SLprint(SEL*p);
void element_intsert(SEL*p,int pos,const sldata* tem);//防止未来数据过大,传入地址 
void element_finre(SEL*p,int pos);
int finselement(SEL*p,const sldata* tem);
void element_modify(SEL*p,int pos,sldata* tem);
void cr_SL(SEL* p)//初始化创建一个顺序表 ,结构体传地址
{
	p->a = (sldata*)malloc(sizeof(sldata)*4);//初始化给四个元素 
	p->sz = 0;//有效数组任然是0; 
	p->capacity = 4;
	return; 
}
void des_SL(SEL*p){
	free(p->a);//记得置为空指针
	p->sz = 0;
	p->capacity = 4;
	return;
}
void dul_SL(SEL* p){
	p->a = (sldata*)realloc(p->a,p->capacity*2*sizeof(sldata));//二倍扩容 
	p->capacity *=2;
	return; 
}

//c_data是对数据的赋值,取决于你的数据类型在这里修改赋值 , 
void c_data(sldata*  t,const sldata* tem){
	*t = (*tem)%11; 
	return;
}//暂时取整形,每个值赋值10以内 ,后续可在此修改,因为成员类型后续也会改变。也可以修改成函数指针的形式,让使用者自主 

 
void first_add(SEL*p,const sldata* tem){
	if(p->sz+2 > p->capacity)//考虑扩容 
	{
		dul_SL(p); 
	}
	memmove(p->a +1,p->a,sizeof(sldata)*(p->sz));
	c_data(p->a,tem);//这里传过去就是首元素 
	p->sz +=1;
	return ; 
}
void end_add(SEL*p,const  sldata* tem)//尾部加一个元素
{
	if(p->sz+2 > p->capacity)//考虑扩容 
	{
		dul_SL(p); 
	}
	c_data(&(p->a[p->sz]),tem);
	p->sz +=1;
	return ; 
} 
void firdelete(SEL*p)
{
	assert(p->sz>0);//注意检查元素个数 
	memmove(p->a,p->a+1,sizeof(sldata)*(p->sz-1));//首部删除,记得放置成空指针 
	p->sz-=1;
	p->a[p->sz]=0;
	return ; 
}
void enddelete(SEL*p)
{
	assert(p->sz>0);
	p->sz-=1;
	p->a[p->sz]=0;
	return ; 
}
void SLprint(SEL*p){
	int i=0;
	for(;i<p->sz;i++)
	{
		printf(" %d ",p->a[i]);//暂时打印整形后续换数据类型可以改变,也可以用函数去打印,,或者借助回调函数 
	}
	printf("\n");
	return ; 
}


void element_intsert(SEL*p,int pos,const sldata* tem)
{
	pos -=1;//输入下标一般是1以上的自然数 
	if(pos == 0)
	{
		first_add(p,tem); 
	 } 
	 else if(pos<=p->sz &&pos>=0 )
	 {
	 	if(p->sz+2 > p->capacity)//考虑扩容 
		{
			dul_SL(p); 
		}
	 	memmove(p->a+pos+1,p->a+pos,(p->sz-pos)*sizeof(sldata));
	 	c_data(&(p->a[pos]),tem);
		p->sz +=1;
	 }
	 else
	 {
	 	assert(0);
	 }
	 return ;
}
void element_finre(SEL*p,int pos)//给出下标删除 
{
	pos--;
	if(!(p->sz>0))
	{
		return ;
	}
	memmove(p->a+pos,p->a+pos+1,(p->sz-pos-1)*sizeof(sldata));
		p->sz -=1;
		return ; 
}


int finselement(SEL*p,const sldata* tem)
{
	int i=0;
	for(;i<p->sz;i++)
	{
		if(p->a[i]==*tem)
		{
			return i;
		}
	}
	return -1;
}

void element_modify(SEL*p,int pos,sldata* tem)
{
	
	c_data(&(p->a[pos-1]),tem);
}



上述代码可以用作参考。

总结

顺序表和链表算是各有优势,在不同的场景具备自己的优劣。熟悉物理结构和实现逻辑更加重要。在之后的队列和栈都是有帮助的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值