链表
链表是线性表的链式存储结构,是用一组任意的存储单元存储线性表的数据元素(存储单元在内存中是不连续的)。
每个数据单元有两部分组成,一个是数据域,存储数据值;另一个是指针域,指向下一个数据单元。这样的数据单元叫做结点。
优点
相比于顺序表,在插入和删除元素时,效率很高;
缺点:
链表的元素是不支持随机访问的,想要知道某个元素,只能从头结点开始遍历整个链表,因此链表访问随机元素的平均时间复杂度是O(n);
由于存储数据需要额外的指针域,所以相比较顺序存储会消耗更多存储单元;
链表可分为单链表、双链表、循环链表,十字链表,今天只总结单链表的基本操作。
单链表结点的定义
struct ListNode
{
int data;
ListNode *next;
};
单链表的创建
创建单链表有两种方法:
- 头插法,按节点的逆序方法逐渐将结点插入到链表的头部。
- 尾插法,按节点的顺序逐渐将节点插入到链表的尾部。
头插法要比尾插法实现起来简单,但是最后创建的链表是逆序的,即第一个输入的节点实际是链表的最后一个节点,这会导致以后使用链表很不方便,因此通常用尾插法来创建链表。
为了操作单链表更加方便,在创建单链表时往往额外添加一个头指针,这个头结点只有指针域,没有数据域,如下图;
下面这图更加清晰的单链表头结点和其他结点的关系:
尾插法创建单链表
//创建单链表
ListNode* createList(int n)
{
ListNode *head, *ptr, *newNode;
head = new ListNode;
//head->data = 0;
head->next = NULL;
ptr = head;
for (int i=0; i<n; i++)
{
newNode = new ListNode;
newNode->data = i;
newNode->next = NULL;
ptr->next = newNode;
ptr = ptr ->next;
}
return head;
}
调用
cout<<"创建单链表:"<<endl;
ListNode *head = createList(6);
dispalyList(head);
单链表输出
void dispalyList(ListNode *head)
{
ListNode *ptr;
ptr = head ->next;
while(ptr != NULL)
{
cout<<ptr->data<<"->";
ptr = ptr -> next;
}
cout<<"NULL"<<endl;
}
创建结果:
单链表删除
void deleteNodeByKey(ListNode *head, int key)
{
ListNode *ptr, *deleteNode;
if (head ->data == key)
{
deleteNode = head;
head = head ->next;
deleteNode = NULL;
}
else
{
ptr = head;
ListNode *tempNode;
while(ptr ->next != NULL && ptr->data != key)
{
tempNode = ptr;
ptr = ptr -> next;
}
deleteNode = ptr;
deleteNode = NULL;
tempNode->next = ptr->next;
}
}
调用
cout<<"删除关键字为 3 的结点:"<<endl;
deleteNodeByKey(head, 3);
dispalyList(head);
cout<<endl;
删除关键字为3的结点:
单链表插入
单链表的插入按插入位置分为三种:
- 在单链表的头部插入(头插法)
- 在单链表的尾部插入(尾插法)
- 插入到单链表已有的关键字的后面
下面这张图同时展示了头插法和尾插法
头插法
void insertNodeInHead(ListNode *head, ListNode *insertNode)
{
if(head == NULL)
{
cout<<"error"<<endl;
return;
}
insertNode->next = head ->next;
head ->next = insertNode;
}
调用
cout<<"插入关键字为 99 的结点:(头插法)"<<endl;
ListNode *insertNodeHead = new ListNode;
insertNodeHead->data=99;
insertNodeHead->next = NULL;
insertNodeInHead(head, insertNodeHead);
dispalyList(head);
cout<<endl;
头插法结果:
尾插法
void insertNodeInTail(ListNode *head, ListNode *inserNode)
{
ListNode *ptr;
if(head == NULL)
{
cout<<"error"<<endl;
return;
}
ptr = head;
while(ptr ->next != NULL)
{
ptr = ptr ->next;
}
ptr->next = inserNode;
}
调用
cout<<"插入关键字为 100 的结点:(尾插法)"<<endl;
ListNode *insertNodeTail = new ListNode;
insertNodeTail->data=100;
insertNodeTail->next = NULL;
insertNodeInTail(head, insertNodeTail);
dispalyList(head);
插入关键字为100的新结点(尾插法):
查找
通过关键字查找相应的结点
ListNode* queryNodeByKey(ListNode *head, int key)
{
if (head ->data == key)
{
return head;
}
else
{
ListNode *ptr;
ptr = head;
while(ptr ->next != NULL)
{
if (ptr->data != key)
{
ptr = ptr -> next;
}
else
{
return ptr;
}
}
}
}
调用
cout<<"查找关键字为 4 的结点:"<<endl;
ListNode *queryNode = queryNodeByKey(head, 4);
cout<<"查找到的结点data为:"<<queryNode->data<<endl;
cout<<endl;
查找结果为: