顺序:
1.先自己敲代码发现不会
2.看思路题解,不要看具体代码
3.再敲,看能不能通过,找问题
4.发现还是不想,对比看具体代码
链表理论基础
链表的存储方式
了解完链表的类型,再来说一说链表在内存中的存储方式。
数组是在内存中是连续分布的,但是链表在内存中可不是连续分布的。
链表是通过指针域的指针链接在内存中各个节点。
所以链表中的节点在内存中不是连续分布的 ,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。
链表的定义
这里我给出C/C++的定义链表节点方式,如下所示:
struct ListNode {
int val; // 节点上存储的元素
ListNode *next; // 指向下一个节点的指针
ListNode(int x) : val(x), next(NULL) {} // 节点的构造函数
};
C/C++最后要自己释放结点内存空间
链表的释放
移除链表元素
题目链接/文章讲解/视频讲解::https://programmercarl.com/0203.%E7%A7%BB%E9%99%A4%E9%93%BE%E8%A1%A8%E5%85%83%E7%B4%A0.html
想法:
struct ListNode* removeElements(struct ListNode* head, int val){
struct ListNode *p=head;
while(head->val==val&&head->next!=NULL&&head!=NULL)
{ head=head->next;}
while(p->next!=NULL&&p!=NULL){
if(p->next->val==val)
p->next=p->next->next;
else
p=p->next;
}
return head;
}
错误:
1.链表最基础的定义和释放不清楚,复习数据结构
2.逻辑不清,导致出现空指针错误
3.
解法:
1.暴力解法
struct ListNode* removeElements(struct ListNode* head, int val){
struct ListNode *p=head;
while(head!=NULL&&head->val==val)
{ head=head->next;}
while(p!=NULL&&p->next!=NULL){
if(p->next->val==val)
p->next=p->next->next;
else
p=p->next;
}
return head;
}
将头结点为所需元素和之后元素为所需元素的情况分开讨论
2.虚拟头结点法
设置一个虚拟头结点,这样原链表的所有节点就都可以按照统一的方式进行移除了。
struct ListNode* removeElements(struct ListNode* head, int val){
struct ListNode* fakehead=(ListNode *)malloc(sizeof(ListNode));
fakehead->next=head;
struct ListNode* cur=fakehead;
while(cur!=NULL&&cur->next!=NULL){
if(cur->next->val==val)
cur->next=cur->next->next;
else cur=cur->next;
}
return fakehead->next;
}
707.设计链表
题目链接/文章讲解/视频讲解:https://programmercarl.com/0707.%E8%AE%BE%E8%AE%A1%E9%93%BE%E8%A1%A8.html
想法
typedef struct MyLinkedList{
int val;
struct MyLinkedList *next;
} MyLinkedList;
MyLinkedList* myLinkedListCreate() {
}
int myLinkedListGet(MyLinkedList* obj, int index) {
int result;
int i;
for(i=1;obj!=NULL;i++){
result=obj->val;
if(i==index)
return result;
else obj=obj->next;
}
return -1;
}
void myLinkedListAddAtHead(MyLinkedList* obj, int val) {
struct MyLinkedList *fakehead=malloc(sizeof(struct MyLinkedList));
fakehead->val=val;
fakehead->next=obj;
}
void myLinkedListAddAtTail(MyLinkedList* obj, int val) {
while(obj!=NULL&&obj->next!=NULL){
obj=obj->next;
}
struct MyLinkedList *addtail=malloc(sizeof(struct MyLinkedList));
obj->next=addtail;
addtail->val=val;
addtail->next=NULL;
}
void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) {
int i;
struct MyLinkedList addindex;
if(index<0)
{
addindex->next=obj;
addindex->val=val;
}
for(i=1;obj!=NULL&&obj->next!=NULL;i++){
if(i==index-1)
obj->next=addindex;
obj->val=val;
else obj=obj->next;
}
if(i==index-1){
obj->next=addindex;
addindex->next=NULL;
addindex->val=val;
}
}
void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
int i;
for(i=1;obj!=NULL;i++){
if(i=index-1){
obj->next=obj->next->next;
}
obj=obj->next;
}
}
void myLinkedListFree(MyLinkedList* obj) {
}
错误:
1.不清楚链表结构定义,命名,释放,复习数据结构
2.出现空指针问题
3.将头指针直接移动
解法:
虚拟头节点法
将头节点为删除或头节点前为插入位置的情况与之后结点为删除或插入位置的情况统一代码
typedef struct MyLinkedList{
int val;
struct MyLinkedList* next;
} MyLinkedList;
MyLinkedList* myLinkedListCreate() {
MyLinkedList* head = (MyLinkedList *)malloc(sizeof (MyLinkedList));
head->next = NULL;
return head;
}
int myLinkedListGet(MyLinkedList* obj, int index) {
if(index<0)
return -1;
MyLinkedList* p;//index可以从0开始
p=obj->next;
int i;
for(i=0;p!=NULL;i++){
if(i==index){
return p->val;
}
else p=p->next;
}
return -1;
}
void myLinkedListAddAtHead(MyLinkedList* obj, int val) {
MyLinkedList *cur=(MyLinkedList *)malloc(sizeof(MyLinkedList));
cur->val=val;
cur->next=obj->next;
obj->next=cur;
}
void myLinkedListAddAtTail(MyLinkedList* obj, int val) {
int i;
MyLinkedList *cur;
cur=obj;
MyLinkedList *Add=(MyLinkedList *)malloc(sizeof(MyLinkedList));
Add->val=val;
Add->next=NULL;
for(i=0;cur->next!=NULL;i++){
cur=cur->next;
}
cur->next=Add;
}
void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) {
int i;
MyLinkedList *p=(MyLinkedList *)malloc(sizeof(MyLinkedList));
p->val=val;
MyLinkedList *cur=obj;
for(i=0;cur!=NULL;i++){
if(i==index){
p->next=cur->next;
cur->next=p;
}
else cur=cur->next;
}
}
void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
MyLinkedList *cur=obj;
int i;
for(i=0;cur->next!=NULL;i++){
if(i==index){
cur->next=cur->next->next;
}
else cur=cur->next;
}
}
void myLinkedListFree(MyLinkedList* obj) {
while(obj != NULL){
MyLinkedList *tmp = obj;
obj = obj->next;
free(tmp);
}}
这道题传入的指针是虚拟头结点的指针,开始以为是头结点指针,而且也不能直接移动头节点指针,要根据每个函数的实际情况,新令一个指针指向虚拟头节点或头节点
206.反转链表
题目链接/文章讲解/视频讲解:https://programmercarl.com/0206.%E7%BF%BB%E8%BD%AC%E9%93%BE%E8%A1%A8.html
想法:
用一个新链表接收旧链表传来的数据,然后将其反向连接
struct ListNode* reverseList(struct ListNode* head){
int i;
struct ListNode *cur=head;
for(i=0;cur!=NULL;i++){
struct ListNode *p=(ListNode *)malloc(sizeof(ListNode));
p->val=cur->val;
if(i==0)
p->next=NULL;
else p->next=p;
cur=cur->next;
}
return p;
}
错误:
1.反向连接时,每次循环p都被重新覆盖了,找不到上一个p的位置
2.在循环里定义的元素出循环后会失效
解法
1.双指针法
struct ListNode* reverseList(struct ListNode* head){
struct ListNode *pre=NULL;
struct ListNode *cru=head;
struct ListNode *temp=NULL;
while(cru!=NULL&&head!=NULL){
temp=head->next;
head->next=pre;
pre=head;
head=temp;
}
return pre;
}
注意:
1.命名的指针必须首先就赋值,不然会变成野指针不能在后面运用
2.要考虑head本身为空的情况
2.递归法
将重复的地方和循环改成了递归(有点小难,二刷再做吧)