数据结构第三课:链表

相对于顺序表这种存放这连续内存地址的而言,每次都需要扩容

然而扩容又有很多缺点:

1、如果扩容扩少了,就需要多次扩容

2、如果扩容扩多了,那又会造成空间浪费

这个时候我们就要想到链表,因为链表和顺序表不一样

链表是由一个个不同的空间组成的,他们在物理结构上是不连续的,但是在逻辑结构上是连续的

由于单项无头结点不循环链表最难,所以我们从这个开始讲起

单链表

typedef  struct    SLNode

{

        typedef  peard;

        typedef*  SLNodenext;

}  

结构体内存放的是数据的内容,还有一个指向地址的指针

单链表的图像如下:

 这个nextcode会一直指向下一个,直到找到NULL为止停下,如此就完成了链表的构建

链表和顺序表一样,也是有头插尾插,头删尾删的功能,如下代码如此实现:

List.c

#include"SList.h"

void SLTPrint(SLNode* phead)

{

SLNode* cur = phead;

while (cur != NULL)

{

printf("%d->", cur->val);

cur = cur->next;

}

printf("NULL\n");

}

SLNode* CreateNode(SLNDataType x)

{

SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));

if (newnode == NULL)

{

perror("malloc fail");

exit(-1);

}

newnode->val = x;

newnode->next = NULL;

return newnode;

}

void SLTPushBack(SLNode** pphead, SLNDataType x)

{

SLNode* newnode = CreateNode(x);

if (*pphead == NULL)

{

*pphead = newnode;

}

else

{

// 找尾

SLNode* tail = *pphead;

while (tail->next != NULL)

{

tail = tail->next;

}

tail->next = newnode;

}

}

void SLTPushFront(SLNode** pphead, SLNDataType x)

{

SLNode* newnode = CreateNode(x);

newnode->next = *pphead;

*pphead = newnode;

}

void SLTPopBack(SLNode** pphead)

{

// 温柔的检查

//if (*pphead == NULL)

// return;

// 空

assert(*pphead);

// 1、一个节点

// 2、一个以上节点

if ((*pphead)->next == NULL)

{

free(*pphead);

*pphead = NULL;

}

else

{

// 找尾

/*SLNode* prev = NULL;

SLNode* tail = *pphead;

while (tail->next != NULL)

{

prev = tail;

tail = tail->next;

}

free(tail);

tail = NULL;

prev->next = NULL;*/

SLNode* tail = *pphead;

while (tail->next->next != NULL)

{

tail = tail->next;

}

free(tail->next);

tail->next = NULL;

}

}

void SLTPopFront(SLNode** pphead)

{

// 空

assert(*pphead);

一个以上节点 + 一个

//SLNode* tmp = (*pphead)->next;

//free(*pphead);

//*pphead = tmp;

// 一个以上节点 + 一个

SLNode* tmp = *pphead;

free(tmp);

*pphead = (*pphead)->next;

}

List.h

 
 

#pragma once

#include<stdio.h>

#include<stdlib.h>

#include<assert.h>

typedef int SLNDataType;

// Single List

typedef struct SListNode

{

SLNDataType val;

struct SListNode* next;

}SLNode;

void SLTPrint(SLNode* phead);

void SLTPushBack(SLNode** pphead, SLNDataType x);

void SLTPushFront(SLNode** pphead, SLNDataType x);

void SLTPopBack(SLNode** pphead);

void SLTPopFront(SLNode** pphead);

SLNode* SLTFind(SLNode* phead, SLNDataType x);

// posǰ

SLNode* SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x);

// ɾposλ

void SLTErase(SLNode** pphead, SLNode* pos);

void SLTDestroy(SLNode** pphead);

Test.c

 
 

#include"SList.h"

void TestSLT1()

{

SLNode* plist = NULL;

SLTPushBack(&plist, 1);

SLTPushBack(&plist, 2);

SLTPushBack(&plist, 3);

SLTPushBack(&plist, 4);

SLTPrint(plist);

SLTPopBack(&plist);

SLTPrint(plist);

SLTPopBack(&plist);

SLTPrint(plist);

SLTPopBack(&plist);

SLTPrint(plist);

SLTPopBack(&plist);

SLTPrint(plist);

//SLTPopBack(&plist);

//SLTPrint(plist);

}

void TestSLT2()

{

SLNode* plist = NULL;

SLTPushFront(&plist, 1);

SLTPushFront(&plist, 2);

SLTPushFront(&plist, 3);

SLTPushFront(&plist, 4);

SLTPrint(plist);

SLTPopFront(&plist);

SLTPrint(plist);

//SLNode* pos = SLTFind(plist, 3);

//SLTInsert(&plist, pos, 30);

}

//int main()

//{

// TestSLT2();

//

// return 0;

//}

struct ListNode

{

struct ListNode* next;

int val;

};

struct ListNode* removeElements(struct ListNode* head, int val)

{

struct ListNode* prev = NULL;

struct ListNode* cur = head;

//while(cur != NULL)

while (cur)

{

if (cur->val == val)

{

struct ListNode* next = cur->next;

free(cur);

if (prev)

prev->next = next;

cur = next;

}

else

{

prev = cur;

cur = cur->next;

}

}

return head;

}

int main()

{

struct ListNode* n1 = (struct ListNode*)malloc(sizeof(struct ListNode));

struct ListNode* n2 = (struct ListNode*)malloc(sizeof(struct ListNode));

struct ListNode* n3 = (struct ListNode*)malloc(sizeof(struct ListNode));

struct ListNode* n4 = (struct ListNode*)malloc(sizeof(struct ListNode));

n1->val = 7;

n2->val = 7;

n3->val = 7;

n4->val = 7;

n1->next = n2;

n2->next = n3;

n3->next = n4;

n4->next = NULL;

struct ListNode* head = removeElements(n1, 7);

return 0;

}

//void Swap(int* p1, int* p2)

//{

// int tmp = *p1;

// *p1 = *p2;

// *p2 = tmp;

//}

//

改变的是int,传的是int*

//int main()

//{

// int a = 0, b = 1;

// Swap(&a, &b);

//

// return 0;

//}

//void Swap(int** p1, int** p2)

//{

// int tmp = *p1;

// *p1 = *p2;

// *p2 = tmp;

//}

//

改变的是int*,传的是int**

//int main()

//{

// int* px = NULL, * py = 0x01;

// Swap(&px, &py);

//

// return 0;

//}

掌握了单链表之后,再来学习其他链表得心应手多了

双向带头循环链表

接下来介绍的是双向带头循环链表,因为除了单链表之外,生活中最常见的就是这种链表了

当然,接下来就是如何实现链表了

List.h

#pragma once

#include<stdio.h>

#include<stdlib.h>

#include<assert.h>

typedef int LTDataType;

typedef struct ListNode

{

struct ListNode* next;

struct ListNode* prev;

LTDataType val;

}LTNode;

LTNode* LTInit();

void LTPrint(LTNode* phead);

void LTPushBack(LTNode* phead, LTDataType x);

void LTPopBack(LTNode* phead);

List.c

 
 

#include"List.h"

LTNode* CreateLTNode(LTDataType x)

{

LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));

if (newnode == NULL)

{

perror("malloc fail");

exit(-1);

}

newnode->val = x;

newnode->next = NULL;

newnode->prev = NULL;

return newnode;

}

LTNode* LTInit()

{

LTNode* phead = CreateLTNode(-1);

phead->next = phead;

phead->prev = phead;

return phead;

}

void LTPrint(LTNode* phead)

{

assert(phead);

printf("ڱλ");

LTNode* cur = phead->next;

while (cur != phead)

{

printf("%d<=>", cur->val);

cur = cur->next;

}

}

void LTPushBack(LTNode* phead, LTDataType x)

{

assert(phead);

LTNode* tail = phead->prev;

LTNode* newnode = CreateLTNode(x);

// phead tail newnode

tail->next = newnode;

newnode->prev = tail;

newnode->next = phead;

phead->prev = newnode;

}

void LTPopBack(LTNode* phead)

{

assert(phead);

//

assert(phead->next != phead);

LTNode* tail = phead->prev;

LTNode* tailPrev = tail->prev;

free(tail);

tailPrev->next = phead;

phead->prev = tailPrev;

}

Test.c

 
 

#include "List.h"

int main()

{

LTNode* plist = LTInit();

LTPushBack(plist, 1);

LTPushBack(plist, 2);

LTPushBack(plist, 3);

LTPushBack(plist, 5);

LTPushBack(plist, 4);

LTPrint(plist);

return 0;

}

其实掌握了这两种链表,在生活中就足以解决大部分的链表问题了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值