目录
对于指针的讲解
前置知识:结构体,基本的指针概念
这篇文章主要讲一下指针的应用例如创建单链表,和循环队列。
在C++中指针是一个比较有意思的东西,它可以有一些比较方便的操作。
指针是一个灵活的变量!即使他存储的是地址那也是一个变量。于是它有和其他变量一样的基础操作:赋值,加减(很遗憾不能乘除,因为没有意义)。
但是指针是一个特殊的变量,因为它可以对该地址存储的变量做一些操作,合起来就非常有意思也非常绕。
开始说最简单的单链表。
单链表的结构:
单链表其实是一个个节点,节点中存储着一个数和一个地址。然后计算机可以找到所有存储的数据,就像一个绳子串着一串珠子,而我们对链表进行操作时就要注意既不要让绳子断掉也不要让绳子打结,这就要求我们对于指针指向的地址心里有数。
先来一个简单的:
#include<iostream>
#define datatype int
using namespace std;
//定义结构体
struct Queue{
Queue* next;
Queue* preNode;
datatype val;
};
//初始化
void InitNode(Queue* &Q)
{
Q=new Queue;
Q->next=nullptr;
Q->preNode=nullptr;
}
//这只是一个示例
int main()
{
Queue* N1;
Queue* N2;
Queue* N3;
InitNode(N1);
InitNode(N2);
InitNode(N3);
//初始化好了
N1->next=N2;
N2->next=N3;
//串好了
N1->next=N1->next->next;
//先看右边N1->next是一个储存着N2地址的指针,他可以对N2进行操作(例如取值)
//就是将右边的地址赋给左边的指针
//相当于N1->next=N3
return 0;
}
难度升级,看一看下面的函数:
//这是一个队列,为了方便运算我创建了一个节点Q,让preNode指针指向队头,next指针指向队尾,
//队列内部是一个双向链表
void push(Queue& Q,datatype target)
{
Queue* N;
InitNode(N);
N->val=target;
if(Q.next==Q.preNode)//队列没有元素,只有一个空的队头
{
Q.next=N;//用Q.next这个指针记录N的地址
Q.preNode->next=N;
//找等号左边,Q.preNode代表指向队头的指针
//前面已经说过指向结构体的指针可以对结构体进行更改
//所以这行代码的意思就是将队头的next指针改为*N的地址
N->preNode=Q.preNode;
//等号左边代表节点*N中存储的preNode指针,右边代表Q.preNode存储的地址
//显然Q.preNode存储的是头节点的地址
}
else
{
N->preNode=Q.preNode;
N->next=Q.preNode->next;
Q.preNode->next=N;
N->next->preNode=N;
}
}
如果你弄明白了,就可以像玩毛线一样对链表进行随意更改了。如果你觉得玩的不过瘾,那么可以在定义结构体时多加上几个指针,这样你就可以玩循环双链表甚至二叉树了,但是你要对每个指针存储的地址心里有数,这样才不会把线弄断或者是打成死结!!!要多动手写代码,这样才会发现一些千奇百怪的错误。
代码:
单链表:
#include<iostream>
using namespace std;
//一个节点,包含数据和下个节点的地址
typedef struct LNode{
int val;//数据
int len;
struct LNode* next;//下一个数据的地址
}LNode,*PLink;
//初始化
void InitList(PLink &pl)
{
pl= new LNode;//申请一片空间,并让l指向这片空间(创立头节点)
pl->next=nullptr;//头节点的下一个地址不存在
pl->len=0;
}
//在指定位置增加节点
void add(LNode* p,int sit,int target)//sit从1开始
{
//LNode* p=&l;
p->len=p->len+1;
for(int i=0;i+1<sit;i++)
{
p=p->next;
}
p->len++;
PLink s;
InitList(s);
s->val=target;
cout<<"插入的数据:"<<s->val<<endl;
s->next=p->next;
p->next=s;
//p.len++;
}
//升序链表的归并排序
PLink SortLink(PLink A,PLink B)
{
PLink p1=A->next;
PLink p2=B->next;
PLink temp=nullptr;
if(p1->val>p2->val) //为了方便后续操作我们要确保p1指向的值不大于p2
{
p1=B->next;
p2=A->next;
}
while(true)
{
if(p1->next==nullptr || p2->next==nullptr) break;
if(p1->next->val>p2->next->val)
{
temp=p1->next;
p1->next=p2;
p1=p1->next;
p2=temp;
}
else p1=p1->next;
}
if(A->next->val>B->next->val) return B;
return A;
}
void Reversion(LNode* &L)//链表的翻转
{
LNode* p1=L->next;
LNode* p2=p1->next;
p1->next=nullptr;
while(p2!=nullptr)
{
LNode* temp=p2->next;
if(temp==nullptr) L->next=p2;
p2->next=p1;
p1=p2;
p2=temp;
}
}
//删除指定位置的节点
void DeletElem(LNode &l,int sit)
{
if(sit<1 || sit>l.len)
{
cout<<"非法删除";
exit(0);
}
LNode* p=&l;
for(int i=0;i+1<sit;i++)
{
p=p->next;
}
LNode* p2=p;
p2=p2->next;
p->next=p2->next;
l.len--;
delete p2;
}
循环双向链表:
#include<iostream>
#define datatype int
using namespace std;
typedef struct LNode{
struct LNode* next;
struct LNode* preNode;
datatype val;
}LNode,*PLink;
void InitList(PLink& L)
{
L=new LNode;
L->preNode=L;
L->next=L;
}
void adds(PLink &L,datatype target)//在头节点后添加节点
{
PLink N;
InitList(N);
N->val=target;
N->next=L->next;
N->preNode=L;
L->next->preNode=N;
L->next=N;
}
void print(PLink L)
{
if(L->next==L)
{
cout<<"只有一个空指针"<<endl;
return;
}
PLink head=L;
cout<<"顺时针打印:";
while(true)
{
L=L->next;
if(L==head) break;
cout<<" "<<L->val;
}
cout<<endl;
cout<<"逆时针打印:";
while(true)
{
L=L->preNode;
if(L==head) break;
cout<<" "<<L->val;
}
cout<<endl;
}
int main()
{
PLink L;
InitList(L);
for(int i=0;i<20;i++)
adds(L,i);
print(L);
return 0;
}
一个很丑的二叉树:
#include<iostream>
#define datatype int
using namespace std;
typedef struct TreeNote{
struct TreeNote* right;
struct TreeNote* left;
datatype val;
}TreeNote,*PTree;
void InitList(PTree& T,datatype target)
{
T=new TreeNote;
T->val=target;
T->left=nullptr;
T->right=nullptr;
}
void adds(PTree pT,datatype target)
{
PTree pN;
InitList(pN,target);
while(true)
{
if(pT->val>pN->val)
{
if(pT->left!=nullptr) pT=pT->left;
else
{
pT->left=pN;
return;
}
}
else
{
if(pT->right!=nullptr) pT=pT->right;
else
{
pT->right=pN;
return;
}
}
}
}
void Midorder(TreeNote* pT)//中序遍历
{
if(pT!=nullptr)
{
Midorder(pT->left);
cout<<pT->val<<" ";
Midorder(pT->right);
}
else return;
}
int main()
{
TreeNote* pT;
InitList(pT,50);
for(int i=40;i<75;i++)
adds(pT,i);
Midorder(pT);
return 0;
}
哈夫曼树:
#include <iostream>
#include<vector>
#include<algorithm>
#define datatype int
using namespace std;
//函数包含比较多,函数收到一个int数组,将其转换为链表,链表排序,合并两个节点,并将新节点加入链表,直到链表只有一个元素
typedef struct Node{
Node* right;
Node* left;
Node* next;
datatype val;
}Node,*PNode;
void InitNode(PNode& N)
{
N=new Node;
N->left=nullptr;
N->right=nullptr;
N->next=nullptr;
}
//判断双链表和树是否出错
void print(PNode L,string type)
{
if(type=="L")
{
L=L->next;
while(L!=nullptr)
{
cout<<L->val<<" ";
L=L->next;
}
cout<<endl;
}
else
{
if(L!=nullptr)
{
print(L->left,"T");
cout<<L->val<<" ";
print(L->right,"T");
}
}
}
//创建双链表
PNode InitList(vector<datatype> nums)
{
sort(nums.begin(),nums.end());//数据由小到大排序
PNode PEnd=nullptr;
PNode L;
InitNode(L);
PEnd=L;//创建头节点和尾指针
for(datatype n: nums)
{
PNode N;
InitNode(N);
N->val=n;
PEnd->next=N;
PEnd=N;
}
return L;
}
PNode CreateHuffmanTree(vector<datatype> nums)
{
PNode L=InitList(nums);
while(true)
{
if(L->next->next==nullptr) break;
//初始化节点N
PNode N;
InitNode(N);
N->val=L->next->val+L->next->next->val;
//把L的头两个节点变为N的孩子节点
N->left=L->next;
N->right=L->next->next;
L->next=N->right->next;
//断掉N的孩子节点同链表的关系
N->left->next=nullptr;
N->right->next=nullptr;
//将N插入到合适的位置
PNode Ptemp=L;
while(true)
{
if(Ptemp->next==nullptr)
{
Ptemp->next=N;
break;
}
if(Ptemp->next->val>=N->val)
{
N->next=Ptemp->next;
Ptemp->next=N;
break;
}
Ptemp=Ptemp->next;
}
}
return L->next;//链表创建时头节点为空节点
}
int main()
{
vector<datatype> nums;
datatype temp;
while(cin>>temp)
{
nums.push_back(temp);
if(cin.get()=='\n') break;
}
PNode T=CreateHuffmanTree(nums);
cout<<"中序遍历二叉树"<<endl;
print(T,"T");
return 0;
}