1. 数组的优缺点
数组元素是连续的存储在有限的内存里,这样有两个缺点:
--数组的大小是有限的。如果需要给数组的内存(缓冲区buffer)扩容,需要调用realloc()函数。realloc()的工作原理:在内部调用malloc()来分配一块新的内存,然后用循环将原先内存的内容拷贝到新内存,然后free()掉原来的内存,返回新内存的地址。
--不方便进行元素的插入与删除。因为元素连续存储,互相之间没有空余的内存。
当然数组也有一个优点:不需要多余的信息就可以依次遍历,因为元素在内存中连续分布。
2. 单链表
单链表是这样一种结构:
它在内存中分散存储,用指针来依次关联。
链表的每个节点都是一个结构体struct,至少包含两个元素。例如:
typedef struct node
{
int n;
struct node * next;
} node;
链表的每个节点(除首末节点外)都用一个指针指向下一个元素,无序,因此链表不能使用二分法来查找。只能遍历。
链表节点的插入与删除 比数组容易多了,因为数组元素连续存储,元素互相之间没有空余的内存。
插入与删除分为三类:
首元素,
中间元素,
尾元素
代码一点点补上。
--------------------------------------------
下面的代码其实用的是C,但是因为.c文件不支持指针的引用,所以就用了.cpp
Slist.cpp
#include "Slist.h"
//建立节点
SListNode*
_buildNode(DataType x)
{
SListNode* tmp = (SListNode*)malloc(sizeof(SListNode));
tmp->data = x;
tmp->next = NULL;
return tmp;
}
// 打印单链表
void
printSlist(SListNode* headNode)
{
SListNode* curNode = headNode;
while (curNode)
{
printf("%d->", curNode->data);
curNode = curNode->next;
}
printf("NULL\n");
}
//尾插
void
pushBack(SListNode* & headNode, DataType x)
{
// 1.空
// 2.不为空
if (headNode == NULL)
{
headNode = _buildNode(x);
}
else
{
// 找尾
SListNode* tail = headNode;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = _buildNode(x);
}
}
// 尾删
void
popBack(SListNode* & headNode)
{
//
// 1.空
// 2.一个节点
// 3.多个节点
//
if (headNode == NULL)
{
return;
}
else if (headNode->next == NULL)
{
free(headNode);
headNode = NULL;
}
else
{
// 找尾
SListNode* tail = headNode;
SListNode* prev = NULL;
while (tail->next)
{
prev = tail;
tail = tail->next;
}
free(tail);
prev->next = NULL;
}
}
//头插
void
pushFront(SListNode* & headNode, DataType x)
{
// 1.空
// 2.不空
if (headNode == NULL)
{
headNode = _buildNode(x);
}
else
{
SListNode* tmp = _buildNode(x);
tmp->next = headNode;
headNode = tmp;
}
}
//头删
void
popFront(SListNode* & headNode)
{
//
// 1.空
// 2.一个节点
// 3.一个以上的节点
//
if (headNode == NULL)
{
return;
}
else if (headNode->next == NULL)
{
free(headNode);
headNode = NULL;
}
else
{
SListNode* tmp = headNode;
headNode = headNode->next;
// free 掉 tmp 指针指向的内存
free(tmp);
}
}
// 插入中间节点
void
insertNode(SListNode* pos, DataType x)
{
assert(pos);
SListNode* tmp = _buildNode(x);
tmp->next = pos->next;
pos->next = tmp;
}
// 删除中间的节点
void
deleteNode(SListNode*& headNode, SListNode* pos)
{
assert(pos);
assert(headNode);
//pos为头结点
if (headNode == pos)
{
headNode = headNode->next;
// free 掉 tmp 指针指向的内存
free(pos);
return;
}
SListNode* prev = headNode;
while (prev)
{
if (prev->next == pos)
{
prev->next = pos->next;
free(pos);
break;
}
prev = prev->next;
}
}
可见,链表需要指针来找到下一个节点,此外,链表每插入一个元素都要malloc,这样做的缺点是,效率低。因为内存分配以及内存中内容的拷贝很占用CPU周期。
Slist.h
#ifndef SLIST_H
#define SLIST_H
#include<stdio.h>
#include<assert.h>
#include<malloc.h>
typedef int DataType;
struct SListNode
{
DataType data; // 数据
struct SListNode* next; //指向下一个节点的指针
};
//建立节点
SListNode*
_buildNode(DataType x);
// 打印单链表
void
printSlist(SListNode* headNode);
//尾插
void
pushBack(SListNode * & headNode, DataType x);
// 尾删
void
popBack(SListNode * & headNode);
//头插
void
pushFront(SListNode * & headNode, DataType x);
//头删
void
popFront(SListNode * & headNode);
// 插入中间节点
void
insertNode(SListNode * pos, DataType x);
// 删除中间的节点
void
deleteNode(SListNode * & headNode, SListNode* pos);
#endif
main.cpp
#include "Slist.h"
int
main(int argc, char const *argv[])
{
SListNode * headNode = NULL;
for(DataType i = 0; i != 10; ++i)
{
pushBack(headNode, i);
printSlist(headNode);
}
for(DataType i = 10; i != 20; ++i)
{
pushFront(headNode, i);
printSlist(headNode);
}
return 0;
}