1.线性表
- 概念:有n(n >= 0)个数据元素组成的有限序列
- 性质:
1)可以在其任意位置上进行插入和删除操作的线性数据结构
2)线性表中的数据存在一对一的关系,即除第1个元素和最后一个元素外,每个元素都有唯一的直接前驱和后继。而第1个元素没有前驱,最后一个元素没有后继。
2.顺序表
- 概念:用一段地址连续的存储单元依次存储数据元素的线性结构
- 模型图:
动态顺序表的实现:
#pragma once
typedef int DataType;
typedef struct SeqList
{
DataType* _a;
size_t _size;
size_t _capacity;
}SeqList;
void SeqListDestroy(SeqList* pSeq)
{
assert(pSeq);
free(pSeq->_a);
pSeq->_a = NULL;
pSeq->_size = pSeq->_capacity = 0;
}
void SeqListInit(SeqList* pSeq, size_t capacity)
{
assert(pSeq && capacity > 0);
pSeq->_a = (DataType*)malloc(capacity*sizeof(DataType));
assert(pSeq->_a);
pSeq->_size = 0;
pSeq->_capacity = capacity;
}
void SeqListPrint(SeqList* pSeq)
{
size_t i = 0;
for (; i < pSeq->_size; ++i)
{
printf("%d", pSeq->_a[i]);
}
printf("\n");
}
void CheckCapacity(SeqList* pSeq)
{
if (pSeq->_size >= pSeq->_capacity)
{
DataType* a = (DataType*)relloc(pSeq->_a, sizeof(DataType)*pSeq->_capacity * 2);
assert(a);
pSeq->_a = a;
pSeq->_capacity *= 2;
}
}
void SeqListPushBack(SeqList* pSeq, DataType x)
{
assert(pSeq);
CheckCapacity(&pSeq);
pSeq->_a[pSeq->_size] = x;
pSeq->_size++;
}
void SeqListPushFront(SeqList* pSeq, DataType x)
{
SeqLisrtEarse(pSeq, 0);
}
void SeqListPopFront(SeqList* pSeq)
{
SeqLisrtEarse(pSeq, 0);
}
void SeqListPopBack(SeqList* pSeq)
{
if (pSeq->_size == 0)
{
printf("SeqList is empty\n");
return;
}
SeqLisrtEarse(pSeq, pSeq->_size - 1);
}
void SeqListInsert(SeqList* pSeq, size_t pos, DataType x)
{
assert(pSeq);
assert(pos <= pSeq->_size);
if (pSeq->_size >= N)
{
printf("SeqList is full\n");
}
else
{
int end = pSeq->_size - 1;
for (; end >= (int)pos;)
{
pSeq->_a[end + 1] = pSeq->_a[end];
--end;
}
pSeq->_a[pos] = x;
pSeq->_size++;
}
}
void SeqLisrtEarse(SeqList* pSeq, size_t pos)
{
assert(pSeq);
assert(pos < pSeq->_size);
if (pSeq->_size == 0)
{
printf("SeqList is empty\n");
}
else
{
int i = pos;
while (i < pSeq->_size - 1)
{
pSeq->_a[i] = pSeq->_a[i + 1];
++i;
}
pSeq->_size--;
}
}
int SeqListFind(SeqList* pSeq, DataType x)
{
int i = 0;
assert(pSeq);
for (; i < pSeq->_size; ++i)
{
if (pSeq->_a[i] = x)
{
return i;
}
}
return -1;
}
void SeqListModify(SeqList* pSeq, size_t pos, DataType x)
{
assert(pSeq && pos < pSeq->_size);
pSeq->_a[pos] = x;
}
void SeqListRemoveAll(SeqList* pSeq, DataType x)
{
size_t index = 0, i = 0, count = 0;
assert(pSeq);
for (; i < pSeq->_size; ++i)
{
if (pSeq->_a[i] != x)
{
pSeq->_a[index] = pSeq->_a[i];
++index;
}
else
{
count++;
}
}
pSeq->_size -= count;
}
void SeqListBubbleSort(SeqList* pSeq)
{
int exchange = 0;
int end = pSeq->_size;
assert(pSeq);
while (end > 1)
{
int i = 0;
for (i; i < end; ++i)
{
if (pSeq->_a[i - 1] > pSeq->_a[i])
{
Swap(&pSeq->_a[i - 1], &pSeq->_a[i]);
exchange = 1;
}
}
if (exchange == 1)
break;
--end;
}
}
void SeqListSelectSort(SeqList* pSeq)
{
}
void SeqListBinarySearch(SeqList* pSeq, DataType x)
{
int left = 0, right = pSeq->_size;
assert(pSeq);
while (left < right)
{
int mid = left + (right - left) >> 1;
if (pSeq->_a[mid] <= x)
left = mid + 1;
else if (pSeq->_a[mid] > x)
right = mid;
else
return mid;
}
return -1;
}
- 数组与顺序表的比较:
1)数组就是相同数据类型的元素按一定顺序排列的集合。也就是说物理上存储在一组连续的地址上,称为数据结构中的物理结构。
2)线性表是数据结构中的逻辑结构,可以存储在数组上,也可以存储在链表上。用顺序存储方法存储的线性表称为顺序表。
总的来说,用数组来存储的线性表就是顺序表。
3.链表
- 概念:链表是线性表的一种,但它不是顺序表。链表的每个节点里都存储着指向下一个节点的指针,把存储数据元素的数据串联起来。根据指针的不同,链表有单链表、双链表和循环链表之分。
- 模型图:
1)单链表是只包含指向下一个节点的指针,只能单向遍历。
2)双链表既包含了指向下一个节点的指针,又包含了指向前一个节点的指针,因此可以双向遍历
3)单向循环链表是将单链表的尾节点和首节点串起来,形成一个环状结构
4)双向循环链表是将双向链表的尾节点和头节点串起来,形成环状结构
单链表的实现:
#pragma once
typedef int DataType;
typedef struct SListNode
{
struct SListNode* _next;
DataType _data;
}SListNode;
SListNode* BuySListNode(DataType x)
{
SListNode* node = (SListNode*)malloc(sizeof(SListNode));
assert(node);
node->_data = x;
node->_next = NULL;
return node;
}
void SListPushBack(SListNode** ppHead, DataType x)
{
if (*ppHead == NULL)
{
*ppHead = BuySListNode(x);
}
else
{
SListNode* cur = *ppHead;
while (cur->_next != NULL)
{
cur = cur->_next;
}
cur->_next = BuySListNode(x);
}
}
void SListPushFront(SListNode** ppHead, DataType x)
{
if (*ppHead == NULL)
{
*ppHead = BuySListNode(x);
}
else
{
SListNode* newNode = BuySListNode(x);
newNode->_next = *ppHead;
*ppHead = newNode;
}
}
void SListPopBack(SListNode** ppHead)
{
if (*ppHead == NULL)
return;
else if ((*ppHead)->_next == NULL)
{
free(*ppHead);
*ppHead = NULL;
}
else
{
SListNode* prev = NULL, *cur = *ppHead;
while (cur->_next)
{
prev = cur;
cur = cur->_next;
}
free(cur);
prev->_next = NULL;
}
}
void SListPopFront(SListNode** ppHead)
{
if (*ppHead == NULL)
return;
else
{
SListNode* next = (*ppHead)->_next;
free(*ppHead);
*ppHead = next;
}
}
void SListDestroy(SListNode** ppHead)
{
SListNode* cur = *ppHead;
while (cur)
{
SListNode* next = cur->_next;
free(cur);
cur = next;
}
*ppHead = NULL;
}
void SListPrint(SListNode* pHead)
{
SListNode* cur = pHead;
while (cur)
{
printf("%d->", cur->_data);
cur = cur->_next;
}
printf("NULL\n");
}
SListNode* SListFind(SListNode* pHead, DataType x)
{
SListNode* cur = pHead;
while (cur)
{
if (cur->_data == x)
{
return cur;
}
cur = cur->_next;
}
return NULL;
}
void SListInsert(SListNode** ppHead, SListNode* pos, DataType x)
{
assert(pos && *ppHead);
if (pos == *ppHead)
{
SListPushFront(ppHead, x);
}
else
{
SListNode* newnode = BuySListNode(x);
SListNode* prev = *ppHead;
while (prev->_next != pos)
{
prev = prev->_next;
}
prev->_next = newnode;
newnode->_next = pos;
}
}
void SListEarse(SListNode** ppHead, SListNode* pos)
{
assert(pos && *ppHead);
if (*ppHead == pos)
{
SListPopFront(ppHead);
}
else if (pos->_next == NULL)
{
SListPopBack(ppHead);
}
else
{
SListNode* prev = *ppHead;
while (prev->_next != pos)
{
prev = prev->_next;
}
prev->_next = pos->_next;
free(pos);
}
}
双向链表的实现:
#pragma once
typedef int DataType;
typedef struct DListNode
{
struct DListNode* _next;
struct DListNode* _prev;
DataType _data;
}DListNode;
DListNode* BuyDListNode(DataType x)
{
DListNode* node = (DListNode*)malloc(sizeof(DListNode));
assert(node);
node->_next = NULL;
node->_prev = NULL;
node->_data = x;
return node;
}
DListNode* DListInit();
DListNode* DListDestroy(DListNode* head);
DListNode* DListPrint(DListNode* head);
void DListPushBack(DListNode* head, DataType x);
void DListPushFront(DListNode* head, DataType x);
void DListPopBack(DListNode* head);
void DListPopFront(DListNode* head);
DListNode* DListFind(DListNode* head, DataType x);
void DListInsert(DListNode* pos, DataType x);
void DListErase(DListNode* pos);
DListNode* DListPrint(DListNode* head)
{
DListNode* cur = head->_next;
while (cur != head)
{
printf("%d->", cur->_data);
cur = cur->_next;
}
printf("HEAD\n");
return NULL;
}
DListNode* DListInit()
{
DataType x = 0;
DListNode* list = BuyDListNode(x);
list->_next = list;
list->_prev = list;
return list;
}
DListNode* DListDestroy(DListNode* head)
{
assert(head);
DListNode* cur = head->_next;
while (cur != head)
{
DListNode* next = cur->_next;
free(cur);
cur = next;
}
free(head);
return head;
}
void DListPushBack(DListNode* head, DataType x)
{
DListInsert(head, x);
}
void DListPushFront(DListNode* head, DataType x)
{
DListInsert(head->_next, x);
}
void DListPopBack(DListNode* head)
{
DListErase(head->_prev);
}
void DListPopFront(DListNode* head)
{
DListErase(head->_next);
}
DListNode* DListFind(DListNode* head, DataType x)
{
DListNode* cur = head->_next;
while (cur != head)
{
if (cur->_data == x)
{
return cur;
}
cur = cur->_next;
}
return NULL;
}
void DListInsert(DListNode* pos, DataType x)
{
assert(pos);
DListNode* prev = pos->_prev;
DListNode* newnode = BuyDListNode(x);
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = pos;
pos->_prev = newnode;
}
void DListErase(DListNode* pos)
{
assert(pos);
DListNode* prev = pos->_prev;
DListNode* next = pos->_next;
next->_prev = prev;
prev->_next = next;
free(pos);
}
- 顺序表和链表比较:
1)顺序表的长度固定,必须在分配内存之前确定数组的长度;链表的长度不固定,可以任意删减
2)顺序表的存储空间连续,支持元素的随机访问;链表的存储空间不连续,数据元素之间使用指针相连,每个数据元素只能访问周围的一个元素;
3)顺序表的存储密度大,内存中存储的全部是数据元素;而链表的存储密度小,每个节点都需要二外存储一个指向下一个节点的指针(双链表则需要两个指针)
4)顺序表在访问特定元素时效率较高,可以使用索引访问,时间复杂度为O(1);链表在访问特定元素时,需要从表头开始,遍历到该元素,时间复杂度为O(n)
5)顺序表在进行插入/删除操作时,会涉及到从当前位置到表尾元素的移动,时间复杂度为O(n);链表在进行插入/删除操作时不需要移动元素,因此时间复杂度为O(1)