回顾:
结构体占据的内存并非成员变量的简单加和,为了点这个智能指针寻址方便,结构体分配内存时有:
对齐:相邻两个成员变量,参照占据内存大的分配内存
补齐:
结构体指针:指向一个结构体的指针
数组>>优化>>动态数组>>队列和栈:保存多个相同类型的数据;
增:在末尾添加速度快,在头部添加速度慢,因为在头部添加需要将数组中所有的数据往后挪一位
删:效率不高,因为需要挪数据。
改:
查:快,只要首地址+偏移量解引用就可以了
链表:插入删除效率高(只需要改变钩子钩的位置就可以了,不需要挪),但是查找效率低(因为查找需要顺藤摸瓜逐步查)
节点:链表的组成部分
内存泄漏:只申请内存但不及时释放内存。
#include <cstdio>;
#include <cstdlib>;
#include <iostream>;
using namespace std;
//1.制作节点类型
struct node{
int data;//数据
struct node* next;//指针
};
//2.在堆内存中创建一个节点并返回节点首地址
node* CreateNode(int data);
//3.尾插法
bool addNode(node* head,int data);
//4.遍历链表的函数
void travel(node* p){
while(p!=NULL){
cout<<p->data<<endl;
p=p->next;
}
return;
}
//5.查找链表中第n个节点,并返回节点首地址,如果找不到返回空
node* getPos(node* head,int n);
//作业:查找链表中第一个值为xxx的节点,并返回节点首地址,如果找不到返回空
//6.插入一个节点到链表中第n个节点后,新节点数据为data,返回插入是否成功
bool insertNode(node* head,int n,int data);
//7.删除链表中第n个节点
bool deleteNode(node* head,int n);
node* getPosByData(node* head,int data);
int main(){
node* head=CreateNode(123);
addNode(head,4546);
addNode(head,678);
addNode(head,488);
addNode(head,4566);
cout<<"插入前的list:"<<endl;
travel(head);
node* test=getPos(head,7);
cout<<"dizhi:"<<test<<endl;
//cout<<"查找到的:"<<test->data<<endl;
node* _test=getPosByData(head,488);
cout<<"查找到的:"<<_test->data<<endl;
node* itest=getPosByData(head,588);
// cout<<"查找到的:"<<itest->data<<endl;
insertNode(head,5,8888);
cout<<"插入后的list:"<<endl;
travel(head);
deleteNode(head,6);
cout<<"删除后的list:"<<endl;
travel(head);
return 0;
}
node* CreateNode(int data){
node* pNew=(node*)malloc(sizeof(node));
pNew->data=data;
pNew->next=NULL;
return pNew;
}
bool addNode(node* head,int data){
//0.排除意外
if(head==NULL) return false;
//1.找到要插入的节点
/*low逼写法
while(1){
if(head->next==NULL) break;//head指向最后一个节点,找到了
head=head->next; //head保存下一个节点首地址
}*/
//功能实现了一定要优化代码,一个对自己要求高的程序员,一定想着把自己的代码写得更好。
//就像写文章,先记流水账,然后提炼成散文,然后提炼成一首诗。
//优雅写法
while(head->next) head=head->next;
//2.把新节点连接到要插入的节点后
head->next=CreateNode(data);
return true;
}
//5.查找链表中第n个节点,并返回节点首地址,如果找不到返回空
node* getPos(node* head,int n){
if(n<=0) return NULL;//防御性编程,一个好的产品都是把用户想的特别特别傻的。
int i;
for(i=0;i<(n-1);i++){
if(head==NULL) return NULL;//注意这一步
head=head->next;
}
return head;
}
//作业:查找链表中第一个值为xxx的节点,并返回节点首地址,如果找不到返回空
node* getPosByData(node* head,int data){
while(head){
if(head->data==data) return head;
head=head->next;
}
cout<<"can not find"<<endl;
return NULL;
}
//6.插入一个节点到链表中第n个节点后,新节点数据为data,返回插入是否成功
//如果只能实现功能,那么只是一个码农的水准,如果你能够自己设计函数,那么就到了设计师的水准。
//如果你能够把整个项目拆分成很多个函数,拆分成很多个类,那么你就是总工程师的水准。
//我们在学习的时候绝对不能以实现功能为最终目的,我们还必须往设计的方面想想。
bool insertNode(node* head,int n,int data){
node* pTemp=getPos(head,n);//获取第n个节点的地址
node* pTemp2=getPos(head,n+1);//获取第n+1个节点的地址
node* _head=NULL;
if(pTemp!=NULL) {
_head=CreateNode(data);//如果能找到第n个节点的首地址,那么创建新节点
pTemp->next=_head;//让第n个节点的next指向新节点
}
else return false;
if(pTemp2){
_head->next=pTemp2;//让新节点的next指向原来的n+1个节点
}
else _head->next=NULL;
return true;
}
//7.删除链表中第n个节点
bool deleteNode(node* head,int n){
if(head==NULL || n<1) return false;
node* pTemp1=getPos(head,n-1);//获取第n-1个节点的地址
node* pTemp2=getPos(head,n);//获取第n个节点的地址
node* pTemp3=getPos(head,n+1);//获取第n+1个节点的地址
if(pTemp2==NULL) return false;
pTemp1->next=pTemp3;
free(pTemp2);
pTemp2->next=NULL;
pTemp2->data=0;
return true;
}
注意,deleteNode函数需要重写,因为这个函数不能删除第一个节点
待重写,有问题的代码:
#include <iostream>
#include <cstdlib>
using namespace std;
//1.制作节点类型
template<typename F>
class node{
F data;//数据
node* next;//指针
public:
//3创建一个节点并返回节点首地址
node* CreateNode(F data);
//尾插法:插入节点到链表的尾部
bool addNode(F data);
bool addNode(node* head,F data);
void travel(node* p);
};
//2第一个节点的首地址能代表整个链表
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
//
int main(int argc, char** argv) {
node<int>* pList=NULL;//第一个节点的首地址
//4.制造链表头节点
node<int> head;
pList=head.CreateNode(123);
//5.尾插法:插入节点到链表的尾部
//head.addNode(345);
//head.addNode(789);
head.addNode(pList,345);
head.addNode(pList,567);
head.travel(pList);
return 0;
}
//创建一个节点并返回节点首地址
template<typename F>
node<F>* node<F>::CreateNode(F data){
//开辟内存
node<F>* pNew=(node<F>*)malloc(sizeof(node<F>)); //malloc开辟的内存段在堆内存中,堆内存不会因为函数结束而释放,只能被手动free,所以可以返回其首地址。
//函数不可以返回函数内部定义的局部变量的地址,因为局部变量占据的内存在函数运行结束之后就自动释放了,返回这种地址没有意义
//初始化
pNew->data=data;
pNew->next=NULL;
// 返回
return pNew;
}
//尾插法:插入节点到链表的尾部
template<typename F>
bool node<F>::addNode(F data){
node<F>* pTemp=this;
//找到要插入的节点
while(1){
if(pTemp->next==NULL) break;//head指向最后一个节点,找到了
pTemp=pTemp->next; //head保存下一个节点首地址
}
//把新节点连接到要插入节点后
node<F> n;
pTemp->next=n.CreateNode(data);
return true;
}
template<typename F>
bool node<F>::addNode(node<F>* head,F data){
//0.排除意外
if(head==NULL) return false;
//1.找到要插入的节点
while(1){
if(head->next==NULL) break;//head指向最后一个节点,找到了
head=head->next; //head保存下一个节点首地址
}
//2.把新节点连接到要插入的节点后
node<F> n;
head->next=n.CreateNode(data);
return true;
}
template<typename F>
void node<F>::travel(node* p){
while(p!=NULL){
cout<<p->data<<endl;
p=p->next;
}
}