一.前提知识:指针
1.指针变量的使用
int a; //声明一个整型变量a
int *p=&a; //声明一个指针类型(int *)p,指向a的地址(&a)
*p=10 //等价于a=10,p此时代表的是a的地址,前面的*是取值符号,即取p存的值,就是a了
2.值传递和引用传递(设实参int b)
void change(int a){} //仅仅将b赋值给a,b再也不管事了,因此修改a与b无关
void change(int &a){} //这里a是b的别名,换了名字改你也在改值
3.数组指针
int a[]={}; int *p=&a[0]; //a代表的就是a[0]的地址,第二句等价int *p=a;
p+i和a+i等价&a[i](地址) *(p+i)和*(a+i)等价a[i](值)
二.链表和基本操作
1.链表基本定义
typedef struct LNode{
ElemType data;
struct LNode *next;
}LINklist,*Lnode;
2.链表基本操作(核心代码)
(1)插入(p插在q后)
p->next=q->next; q->next=p;
(2)删除(删除p)
q->next=p->next; delete p;
(3)创建(尾插法)
ListNode *createByTail(){
ListNode *head,*p1,*p2;//p2尾节点,p1新节点
int num;
cin>>num;
head=new ListNode;
head->next=NULL;
p2=head;
while(num){
p1=new ListNode;
p1->num=num;
p2->next=p1;
p2=p1;
cin>>num;
}
p2->next=NULL;
return head;
}
3.循环链表
(1)避免死循环的循环条件
p->next!=NULL; ——> p->next!=head;
(2)经典应用——约瑟夫环
void josephus(ListNode *head,int m){
int length=getLength(head);
ListNode *cur=head->next;
ListNode *pre=head;
int count=1;
while(length>1){
if(count==m){
ListNode *temp=cur;
cur=cur->next;
pre->next=cur;
count=1;
delete temp;
length--;
}
else if(count<m){
cur=cur->next;
pre=pre->next;
count++;
}
if(cur==head){
cur=cur->next;
pre=pre->next;
}
}
cout<<"留下:"<<cur->num<<endl;
}
4.双向链表
(1)结点定义
struct DulNode{
int data;
struct DulNode *prior;
struct DulNode *next;
}
(2)插入操作(s所指向结点插到p前)
s->prior=p->prior;
p->prior->next=s;
s->next=p;
p->prior=s;
(3)删除操作(删除p所指向结点)
p->prior->next=p->next;
p->next->prior=p->prior;
三.经典例题
单链表逆置
ListNode* reverseList(ListNode* head){
ListNode *new_head=NULL;
while(head){
ListNode *next=head->next;
head->next=new_head;
new_head=head;
head=next;
}
return new_head;
}
一般做题三步: 摘结点——>找位置——>插入
四.特殊思想——快慢指针
某些题目直接暴力求解复杂度会很高,快慢指针的思想可以大幅降低时间复杂度。
如:求链表的倒数第k个节点,快指针先走k步,快慢指针再一起走,快指针到尾,慢指针所指向便是所求结点。
相关题目:求链表中间结点、旋转链表、链表交点、链表求环。