先说下使用链表应该注意什么:
1.因为链表是用指针表示的,所以进函数时候一定要判断head指针是否为NULL,在进行删除之后也要注意会不会链表为空了,如果调用一个NULL的指针会发生异常
2.无论增删改都要注意在头部跟在尾部两种情况,因为他们跟在中间的处理方法是不同的
3.增加要先连后断, 删除要先接后删
1.首先放最基础的创建、增删改
struct node
{
int num;
struct node *next;
node(int x) : num(x), next(NULL){}
};
struct node* create() //直接按照输入顺序插入,如果想按照排序顺序直接在函数里调用Insert函数
{
struct node *head = NULL, *p = NULL, *tail = NULL;
int num, n, sz = sizeof(struct node);
scanf("%d", &n);
for(int i = 1; i <= n; i++)
{
scanf("%d", &num);
p = (struct node *)malloc(sz)
p->num = num;
//顺序插入
p->next = NULL;
if(head == NULL)
head = p;
else
tail->next = p;
tail = p;
/*
逆序插入
p -> next = head;
head = p; //等于每次都在前面添加节点 第一个进来的是最后一个节点
*/
}
return head;
}
struct node* Insert(struct node *head, struct node *stud)
{
struct node *pre = NULL, *buf, *cur;
cur = stud;
buf = head;
if(head == NULL) //头结点插入
{
head = cur;
head->next = NULL;
}
else //这里逻辑一定要清晰
{
while((cur->num > buf->num && buf->next != NULL))
pre = buf,buf = buf->next;
if(cur->num <= buf->num)
{
if(buf == head) head = cur;
else pre->next = cur; //这时 buf指向原来head的地址,head指向cur的地址
cur->next = buf;
}
else
buf->next = cur, cur->next = NULL;
}
}
struct node* Delete(struct node *head, int num)
{
struct node *pre = NULL, *buf = NULL;
if(head == NULL) return NULL;
if(head->num == num)
{
pre = head;
head = head->next;
free(pre);
}
if(head == NULL) return NULL;//一定要看下删完了,是不是NULL,如果是NULL再调用会异常
pre = head;
buf = head->next;
while(buf != NULL)
{
if(buf->num == num)
{
pre->next = buf->next;
free(buf);
}
else
pre = buf;
buf = pre -> next; //因为buf可能被释放,所以这里要是pre->next
}
return head;
}
void Print(struct node *head)
{
if(head == NULL) return;
struct node *cur;
for(cur = head; cur!=NULL; cur = cur->next)
printf("%d\n", cur->num);
}
2.在不知道头结点的情况下,如何删除一个已知节点
因为我们不知道头结点,所以根本不可能知道该节点之前节点的信息,只能知道之后的信息,而删除节点又必须是将前后两节点相连,所以我们只需要把要删除节点的下一个节点的信息都赋值给该节点,然后删除那个节点
node *p; // 当前节点
node *q;
q = p -> next;
p.data = q.data; // 复制q节点到p
p -> next = q -> next; // 删除q
free(q);
3.将链表逆序
思路:两个指针,一个往前进行头插入,另一个往后遍历获取结点,原来的头结点head不变,每次获取一个要前插的结点之后,将头节点跟获取的节点的下一个节点相连,把当前节点头插入到pre前面,更新pre的位置
vector<int> printListFromTailToHead(struct ListNode* head)
{
vector<int> vec;
ListNode *buf=head;
ListNode *pre=buf;
if(head==NULL)
return vec;
while(head->next!=NULL)
{
buf=head->next;
head->next=buf->next;
buf->next=pre;
pre=buf;
}
while(buf)
{
vec.push_back(buf->val);
buf=buf->next;
}
return vec;
}
4.只遍历一遍链表,输出倒数第K个元素
2个指针,一开始都指向head,然后后面指针先往后k-1个位置,然后两者同时移动,后面指针到头了,前面指针就是倒数第k个位置,一定注意,链表题先看head是不是null,在删除以及移动过程中注意是不是null
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
struct ListNode *pre, *buf;
if(pListHead == NULL) return NULL;
pre = pListHead;
buf = pListHead;
k--;
while(k--)
{
buf = buf->next;
if(buf == NULL) return NULL;
}
while(buf->next != NULL) buf = buf->next, pre = pre->next;
return pre;
}
5.合并两个有序的链表
这里一定记得函数开始先开 如果有一方是null怎么办,如果不在while前看,index可能会指针飞掉,因为可能当时index并没有赋值
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
ListNode *index1, *index2, *index, *head;
head = NULL;
index1 = pHead1;
index2 = pHead2;
index = NULL;
if(index1 == NULL) return index2;
if(index2 == NULL) return index1;
while(1)
{
if(index1->val <= index2->val)
{
if(head == NULL) head = index1, index = head;
else index -> next = index1, index = index->next;
index1 = index1->next;
}
else
{
if(head == NULL) head = index2, index = head;
else index -> next = index2, index = index->next;
index2 = index2 -> next;
}
if(index2 == NULL && index1 == NULL) break;
if(index1 == NULL && index2 != NULL)
{
index->next = index2;
break;
}
if(index2 == NULL && index1 != NULL)
{
index->next = index1;
break;
}
}
return head;
}
也可以写成递归的:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if(pHead1 == NULL) return pHead2;
if(pHead2 == NULL) return pHead1;
if(pHead1->val <= pHead2->val)
{
pHead1 -> next = Merge(pHead1->next, pHead2);
return pHead1;
}
else
{
pHead2 -> next = Merge(pHead1, pHead2->next);
return pHead2;
}
}