使用链表存储信息
- 链表
- 创建链表
- 向链表中插入值(单向链表)
- 删除链表中的值(单向链表)
- 反向遍历链表
- 找出中间节点
- 找出倒数第K个节点
- 翻转链表
链表
链表是一种数据结构,由连接在一起的小容器组成。这些容器称为节点。链表中第一个节点称为头节点,最后一个称为尾节点。
链表有三种基本形式,从最简单到最复杂依次为:
- 单链表
- 双链表
- 树
创建链表
参考:/qq_41620518/article/details/81143414
/u011579908/article/details/68943524
我们以单链表为例:
step1: 先创建节点(单链表的节点包括:数据域和指针域)
struct ListNode{
int m_key;
ListNode* next;
};
节点也可以是类:
class Node //节点类
{
public:
Node() {}//三种不同的节点创建方式
Node(int n) { num = n; next = NULL; }
Node(int n, Node *p) { num = n; next = p; }
void setNum(int n) { num = n; }
void setNext(Node *p) { next = p; }
int getNum() { return num; }//利用set&&get函数来进行操作
Node *getNext() { return next; }
private:
int num;//此处仅存储了一个整数,实际上数据可以是任何形式。
Node *next;//此处存放下一个指针
};
然后是创建链表:
void createList(ListNode* pHead){
ListNode* p = pHead;
for (int i = 1; i < 10; ++i) {
ListNode* pNewNode = new ListNode;
pNewNode->m_key = i; // 将新节点的值赋值为i
pNewNode->next = NULL;
p->next = pNewNode; // 上一个节点指向这个新建立的节点
p = pNewNode; // p节点指向这个新的节点
}
}
下面以一段代码来说明创建链表的过程:
#include <iostream>
using namespace std;
/* 创建一个单链表 */
struct ListNode{
int m_key;
ListNode* next;
};
void createList(ListNode* pHead){
ListNode* p = pHead;
for (int i = 1; i < 10; ++i) {
ListNode* pNewNode = new ListNode;
pNewNode->m_key = i; // 将新节点的值赋值为i
pNewNode->next = NULL;
p->next = pNewNode; // 上一个节点指向这个新建立的节点
p = pNewNode; // p节点指向这个新的节点
}
}
int main(){
ListNode* head = NULL;
head = new ListNode;
head->m_key = 0;
head->next = NULL;
createList(head);
return 0;
}
从main()函数开始,先是定义了一个ListNode结构类head的指针,并初始化为NULL;其后为head分配内存,初始化类成员,接着就是进入到创建链表的函数中。
- 1.将pHead的地址赋予一个类的新指针p,此时p的地址与pHead的地址一样,对p操作就是对pHead操作;
ListNode* p = pHead;
-
2、进入for循环,此时i=1
-
3、创建一个类的新指针pNewNode,作为中间过程为以后存放新节点提供过渡;
ListNode* pNewNode = new ListNode;
- 4、赋值,令此时的pNewNode的m_key为1,next地址为null;
pNewNode->m_key = i; // 将新节点的值赋值为i
pNewNode->next = NULL;
- 5、让指针p的next指向pNewNode,此时,由于第一步,因此pHead的next地址也指向pNewNode。这样就实现了pHead的next指向链表中的第一个节点;
p->next = pNewNode; // 上一个节点指向这个新建立的节点
- 6、将pNewNode的地址赋予p,即将pNewNode的m_key以及next赋予p->m_key为1;p->next为null,此时由于pHead的内存是由new来分配的,其地址不由p改变而改变,因此此时p的地址与pHead的地址不一样,对p操作不咋在等同于对pHead操作,也正因为这样,pHead的next地址将永远指向i=1时的pNewNode,即链表中的第一个节点;
p = pNewNode; // p节点指向这个新的节点
- 7、当i=2时,又用new新建一个pNewNode,此时需要注意,此时的pNewNode与i=1时新建的pNewNode的地址不一样,即为不同的类对象。因此我们可以将i=1时新建的pNewNode人为的称为pNewNode1,i=2时称为pNewNode2;
ListNode* pNewNode = new ListNode;
- 8、对pNewNode2->m_key与next赋值;
pNewNode->m_key = i; // 将新节点的值赋值为i
pNewNode->next = NULL;
- 9、将pNewNode的地址赋予p->next 。此时需要注意,由于第6步,p的地址为pNewNode1的地址,因此对p操作就相当于对pNewNode1操作,所以此时pNewNode1的next地址即为pNewNode2的地址,这样就实现了第一个节点的next指向第二个节点。
p->next = pNewNode; // 上一个节点指向这个新建立的节点
- 10、将pNewNode2的地址赋予p;
p = pNewNode; // p节点指向这个新的节点
- 11、以此类推。
下面参考这篇文章 /byonecry/p/
向链表中插入值(单向链表)
//p节点后插入值为i的节点
void insertNode(Node *p, int i){
Node* node = new Node;
node->value = i;
node->next = p->next;
p->next = node;
}
删除链表中的值(单向链表)
当需要删除一个节点p时,只需要将p的前一个节点的next赋为p->next,但是由于是单向的,只知道p,无法知道p前一个节点,所以需要转换思路。将p下一个的节点覆盖到p节点即可,这样就等效于将p删除了。
void deleteNode(Node *p){
p->value = p->next->value;
p->next = p->next->next;
}
反向遍历链表
法一:反向遍历链表就类似于事先遍历的节点后输出,即“先进后出”,那么可以将链表遍历存放于栈中,其后遍历栈依次弹出栈节点,达到反向遍历效果。
法二:递归
//
void printLinkedListReversinglyByStack(Node *head){
stack<Node* > nodesStack;
Node* pNode = head;
//遍历链表
while (pNode != NULL) {
nodesStack.push(pNode);
pNode = pNode->next;
}
while (!nodesStack.empty()) {
pNode=nodesStack.top();
printf("%d\t", pNode->value);
nodesStack.pop();
}
}
//2.递归
void printLinkedListReversinglyRecursively(Node *head){
if (head!=NULL) {
if (head->next!=NULL) {
printLinkedListReversinglyRecursively(head->next);
}
printf("%d\t", head->value);
}
}
找出中间节点
用slow和fast指针标记,slow每次走一步,fast每次走两步,当fast到尾节点时,slow就相当于总长度的一半,即在中间节点。
//找出中间节点
Node* findMidNode(Node* head){
Node* slow = head;
Node* fast = head;
while (fast->next != 0&&fast->next->next!=0) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
找出倒数第K个节点
用slow和fast指针标记,fast指针事先走k步,然后slow和fast同时走,当fast到达末节点时,slow在fast的前k个节点,即为倒数第k个节点。
//找出倒数第k个节点
Node* findKNode(Node* head,int k){
Node *temp1 = head;
Node *temp2 = head;
while (k-->0) {
if(temp2 == NULL){
return NULL;
}
temp2 =temp2->next;
}
while (temp2->next != NULL&&temp2->next->next!=NULL) {
temp1 = temp1->next;
temp2 = temp2->next;
}
return temp1;
}
翻转链表
参考这篇文章 /wanglei5205/p/
//翻转链表
Node * reverseLinkedList(Node* head,int k){
Node *reversedHead = NULL;
Node *pNode = head;
Node *pre = NULL;
while (pNode!=NULL) {
if (pNode->next==NULL) {
reversedHead = pNode;
}
Node* nxt = pNode->next;
pNode->next = pre;
pre=pNode;
pNode=nxt;
}
return reversedHead;
}