赞
踩
大家好,今天我们来学习数据结构中的顺序表与链表!源码在最后附上
首先我们先来认识一下顺序表:
**如上图所示:很多人会以为数组就是顺序表,顺序表就是数组,这种理解时错误的。
数组:
数组是相同数据类型的元素按一定顺序排列的的集合。数组中的元素存储在一个连续性的内存块中,并通过索引来访问。
简单的说,数组是在物理空间中连续存储的相同数据类型的元素的集合。
而顺序表:
顺序表,全名顺序存储结构,是线性表的一种。(线性表(linear list)是数据结构的一种,一个线性表是 n 个具有相同特性的数据元素的有限序列。线性表在逻辑上是线性结构,也就说是连续的一条直线,但是在物理结构上并不一定是连续的。常见的线性表:顺序表、链表、栈、队列、字符串等。)
顺序表不仅要求数据在逻辑上是连续的一条直线,还要求用一段物理地址连续的存储单元以存储表中数据元素,一般情况下采用数组存储。
总结:
顺序表是在计算机内存中以数组的形式保存的线性表,只能从头开始连续存储。
此处将「数组」理解为物理结构,「顺序表」理解为逻辑结构比较合理
我们可以用数组实现顺序表,但我们同样可以用数组实现二叉树、队列等结构,因此不能直接认为顺序表就是数组
在顺序表中我们可以定义表中元素个数、表空间大小等变量,在数组中是没有的
首先我们来学习一下顺序表的静态存储:
而顺序表的静态存储又存在明显的缺陷和不足:
不知道需要给确定的多少个空间,N给小了不够用,N给大了会浪费。
那么如何解决这个问题呢?
在这里我们使用malloc来动态开辟空间,再使用realloc来进行空间的管理。
顺序表的动态存储:
在这里我们要注意realloc扩容存在异地扩容和原地扩容俩种情况;
注意地址,当你realloc的空间没有那么大时,这里就是原地扩容:
注意地址,当你realloc的空间很大的时候,就会异地扩容:
这里先让我们简单的来实现顺序表的几个功能:(完整功能最后附上源码)
实现顺序表的头插,即往前边插进去数据:
实现顺序表中间插入数据:
实现顺序表的删除:
通过以上学习,我们大概了解了顺序表的一些基础知识,那么顺序表是否存在一些缺点呢?
顺序表的缺点:
1.尾部插入数据还不错,但是头部和中间插入删除数据,效率低下,需要挪动数据。
2.满了之后只能扩容,而扩容是有一定的消耗的,扩容一般是存在一定的空间浪费(假如空间100已经满了,扩容到200,只需要插入120个数据,那么就有80个浪费掉了)
3.一次扩的多了,可能浪费掉了;一次扩的少了,有需要频繁去扩容。
接下来让我们来学习链表:
链表:链表采用链式存储结构,其内存空间不连续
而链表分为单链表和双链表,我们先来学习单链表:
它是由一个一个结点结合在一起的,而每个节点的地址没有关联,都是随机的,东一个西一个。
注意单链表的最后一个结点指向NULL;
这里我们简单的定义一个单链表:
如何去访问链表里的各个结点呢:我们定义一个cur指针指向单链表的头节点,然后通过cur->next来依次访问剩下的结点,如果cur==NULL,说明链表访问完了。
接下来我们来实现单链表几个基本的功能:(完整源码结尾附上)
单链表的尾插:
这里需要额外注意:因为phead是plist的临时拷贝,使用要注意二级指针与一级指针的使用,当然不使用二级指针也是可以 ,也有不同的写法,这里这样写是让大家小心注意一下其中的大坑。
单链表的头插:
单链表的尾删:
需要注意,这里需要区分一个结点和多个结点的不同情况。
单链表的头删:
这里需要注意:我们的思路是把头节点的指向原本头结点的下一个结点,然后还需要free掉原本的头节点,这里会出现一种很常见的错误:
先free掉tmp后,就没办法再进行头结点的链接,丢失了地址。
而正确的写法:(大家注意区分)
关于链表还有一个知识点,就是哨兵位:
哨兵位就是在头节点的前面给它再加一个结点,让它充当头节点,但它不存储具体的数据,只是更方便进行头插头删等操作,哨兵位的含金量不是很高,有时候方便用来刷OJ题,这里大家简单了解下,有兴趣的朋友自己敲敲代码试一下把。
这里我们先再来了解下链表有哪些分类:
我们注意到,单链表和双链表的区别是:单链表一个结点只能指向下一个结点,而双边表一个结点可以指向前一个结点也可以指向后一个结点。
而循环链表的特殊之处则是尾结点直接指向了头节点,实现了一个循环。
接下来我们先来比较一下俩种链表:
我们主要学习一下带头双向循环链表:
根据上面单链表的学习,我们简单写几个功能让加强大家对双向链表的理解:
带头双向循环链表的尾插:
我们可以明显感受到,双向循环链表的数据处理只需要几个简单的指向就可以简单完成;
带头双向循环链表的尾删:
知识的分享就结束了,接下来附上顺序表与链表的源码:(因为内容较多,所以不是很全,但有具体的框架,大家可以根据自己的需要进行补全)
顺序表:
SeqList.h
- #pragma once
- #include<stdio.h>
- #include<stdlib.h>
- #include<assert.h>
-
-
- typedef int SLDataType;
-
- typedef struct SeqList
- {
- SLDataType* a;
- int size; // 有效数据
- int capacity; // 空间容量
- }SL;
-
- void SLInit(SL* psl);
- void SLDestroy(SL* psl);
-
- void SLPrint(SL* psl);
- void SLCheckCapacity(SL* psl);
-
- // 头尾插入删除
- void SLPushBack(SL* psl, SLDataType x);
- void SLPushFront(SL* psl, SLDataType x);
- void SLPopBack(SL* psl);
- void SLPopFront(SL* psl);
-
- // 任意下标位置的插入删除
- void SLInsert(SL* psl, int pos, SLDataType x);
- void SLErase(SL* psl, int pos);
-
SeqList.c
- #include"SeqList.h"
-
- void SLInit(SL* psl)
- {
- assert(psl);
-
- psl->a = NULL;
- psl->size = 0;
- psl->capacity = 0;
- }
-
- void SLDestroy(SL* psl)
- {
- assert(psl);
-
- if (psl->a != NULL)
- {
- free(psl->a);
- psl->a = NULL;
- psl->size = 0;
- psl->capacity = 0;
- }
- }
-
- void SLPrint(SL* psl)
- {
- assert(psl);
-
- for (int i = 0; i < psl->size; i++)
- {
- printf("%d ", psl->a[i]);
- }
- printf("\n");
- }
-
- void SLCheckCapacity(SL* psl)
- {
- assert(psl);
-
- if (psl->size == psl->capacity)
- {
- int newCapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;
- SLDataType* tmp = (SLDataType*)realloc(psl->a, sizeof(SLDataType)*newCapacity);
- if (tmp == NULL)
- {
- perror("realloc fail");
- return;
- }
-
- psl->a = tmp;
- psl->capacity = newCapacity;
- }
- }
-
- // 10:37
- void SLPushBack(SL* psl, SLDataType x)
- {
- assert(psl);
-
- SLCheckCapacity(psl);
-
- psl->a[psl->size] = x;
- psl->size++;
- }
-
- void SLPushFront(SL* psl, SLDataType x)
- {
- assert(psl);
-
- SLCheckCapacity(psl);
-
- // 挪动数据
- int end = psl->size - 1;
- while (end >= 0)
- {
- psl->a[end + 1] = psl->a[end];
- --end;
- }
-
- psl->a[0] = x;
- psl->size++;
- }
-
- void SLPopBack(SL* psl)
- {
- assert(psl);
-
- // 空
- // 温柔的检查
- /*if (psl->size == 0)
- {
- return;
- }*/
-
- // 暴力检查
- assert(psl->size > 0);
-
- //psl->a[psl->size - 1] = -1;
- psl->size--;
- }
-
- // 10:47
- void SLPopFront(SL* psl)
- {
- assert(psl);
-
- // 暴力检查
- assert(psl->size > 0);
-
- int begin = 1;
- while (begin < psl->size)
- {
- psl->a[begin - 1] = psl->a[begin];
- ++begin;
- }
-
- psl->size--;
- }
-
- // 注意pos是下标
- // size是数据个数,看做下标的话,他是最后一个数据的下一个位置
- void SLInsert(SL* psl, int pos, SLDataType x)
- {
- assert(psl);
- assert(pos >= 0 && pos <= psl->size);
-
- SLCheckCapacity(psl);
-
- // 挪动数据
- int end = psl->size - 1;
- while (end >= pos)
- {
- psl->a[end + 1] = psl->a[end];
- --end;
- }
-
- psl->a[pos] = x;
- psl->size++;
- }
-
- void SLErase(SL* psl, int pos)
- {
- assert(psl);
- assert(pos >= 0 && pos < psl->size);
-
- // 挪动覆盖
- int begin = pos + 1;
- while (begin < psl->size)
- {
- psl->a[begin - 1] = psl->a[begin];
- ++begin;
- }
-
- psl->size--;
- }
Test.c:
- #include<stdio.h>
- #include"SeqList.h"
-
-
- void TestSL1()
- {
- SL sl;
- SLInit(&sl);
- SLPushBack(&sl, 1);
- SLPushBack(&sl, 2);
- SLPushBack(&sl, 3);
- SLPushBack(&sl, 4);
- SLPushBack(&sl, 5);
- SLPushBack(&sl, 6);
- SLPushBack(&sl, 7);
- SLPushBack(&sl, 8);
- SLPushBack(&sl, 9);
- SLPrint(&sl);
-
- SLPushFront(&sl, 10);
- SLPushFront(&sl, 20);
- SLPushFront(&sl, 30);
- SLPushFront(&sl, 40);
- SLPrint(&sl);
-
- SLDestroy(&sl);
- }
-
- void TestSL2()
- {
- SL sl;
- SLInit(&sl);
- SLPushBack(&sl, 1);
- SLPushBack(&sl, 2);
- SLPushBack(&sl, 3);
- SLPushBack(&sl, 4);
- SLPushBack(&sl, 5);
- SLPrint(&sl);
-
- SLPopBack(&sl);
- SLPrint(&sl);
-
- SLPopBack(&sl);
- SLPopBack(&sl);
- SLPopBack(&sl);
- SLPopBack(&sl);
- SLPrint(&sl);
-
-
-
- SLPushFront(&sl, 10);
- SLPushFront(&sl, 20);
- SLPushFront(&sl, 30);
- SLPushFront(&sl, 40);
- SLPrint(&sl);
-
- SLDestroy(&sl);
- }
-
- void TestSL3()
- {
- SL sl;
- SLInit(&sl);
- SLPushBack(&sl, 1);
- SLPushBack(&sl, 2);
- SLPushBack(&sl, 3);
- SLPushBack(&sl, 4);
- SLPushBack(&sl, 5);
- SLPrint(&sl);
-
- SLPopFront(&sl);
- SLPrint(&sl);
-
- SLPopFront(&sl);
- SLPrint(&sl);
-
- SLPopFront(&sl);
- SLPrint(&sl);
-
- SLPopFront(&sl);
- SLPrint(&sl);
-
- SLPopFront(&sl);
- SLPrint(&sl);
-
- //SLPopFront(&sl);
- //SLPrint(&sl);
- }
-
- void TestSL4()
- {
-
-
- SL sl;
- SLInit(&sl);
- SLPushBack(&sl, 1);
- SLPushBack(&sl, 2);
- SLPushBack(&sl, 3);
- SLPushBack(&sl, 4);
- SLPushBack(&sl, 5);
- SLPrint(&sl);
-
- SLInsert(&sl, 2, 20);
- SLPrint(&sl);
-
- SLInsert(&sl, 6, 20);
- SLPrint(&sl);
-
- SLInsert(&sl, 0, 20);
- SLPrint(&sl);
-
- SLInsert(&sl, 10, 20);
- SLPrint(&sl);
-
- SLDestroy(&sl);
- }
-
- void TestSL5()
- {
- SL sl;
- SLInit(&sl);
- SLPushBack(&sl, 1);
- SLPushBack(&sl, 2);
- SLPushBack(&sl, 3);
- SLPushBack(&sl, 4);
- SLPushBack(&sl, 5);
- SLPrint(&sl);
-
- SLErase(&sl, 3);
- SLPrint(&sl);
-
- SLErase(&sl, 3);
- SLPrint(&sl);
-
- //SLErase(&sl, 3);
- //SLPrint(&sl);
- }
-
- int main()
- {
- TestSL5();
- return 0;
- }
单链表:
SList.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);
SList.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;
- free(tmp);
- *pphead = (*pphead)->next;
- }
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;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。