#pragma once
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include <stdlib.h>
typedef int DataType;
//定义一个结构体类型
typedef struct LinkNode
{
DataType data;//定义节点的数据
struct LinkNode *next;//保存下一个类型节点的地址
}LinkNode, *pLinkNode, *pList;//指向节点的指针
void InitLinkList(pList* pHead);
void Destroy(pList *pHead);
void PushBack(pList* pHead, DataType x);
void PopBack(pList* pHead);
void PushFront(pList* pHead, DataType x);
void PopFront(pList* pHead);
void PrintList(pList list);
int GetListLength(pList head);
pLinkNode Find(pList head, DataType x);
void Insert(pList *pHead, pLinkNode pos, DataType x);
void Remove(pList *pHead, DataType x);
void RemoveAll(pList *pHead, DataType x);
void Erase(pList *pHead, pLinkNode pos);
void ReverseList(pList *pHead);
void EraseNotTail(pLinkNode pos);
//1 pHead是单链表的头指针,它指向表中第一个节点,若phead==null表示单链表是一个空表
void InitLinkList(pList* pHead)//pHead是单链表的头指针,它指向表中第一个节点,若phead==null表示单链表是一个空表
{
assert(pHead);
(*pHead)->next=NULL;
}
//2 由于在后边的 几个函数中都需要创建一个新的节点,所以将创建这个节点的过程直接分装成一个函数,使得程序简洁明了
pLinkNode BuyNode(DataType x)
{
pLinkNode newNode=(pLinkNode)malloc(sizeof(LinkNode));
//创建一个新节点
    newNode->data=x;
newNode->next=NULL;
return newNode;
}
//3 尾插法建立单链表,首先判断当前链表是否为空,若为空
//  则直接插入节点,否则逐次遍历,将最后一个节点的指针域赋值要插入的节点
void PushBack(pList* pHead, DataType x)
{
assert(*pHead);
pLinkNode cur=*pHead;
pLinkNode newNode=BuyNode(x);
   //尾部插入
if(cur==NULL)
{
  cur=newNode;
  return;
}
else
{	
while(cur->next)//有节点
{
  cur=cur->next;
}
cur->next=newNode;
}
}
//3尾删方法删除单链表,首先判断,
//(1)若当前链表为空则不用删除直接返回
//(2)若链表只有一个节点(*pHead)->next==NULL,则释放*pHead,即可达到删除下一个节点的效果
//(3)若链表含有两个或两个以上的节点,则逐个遍历,当出现当前遍历的节点cur->next->next为空的时候通过free(cur->next)即可删除最后一个节点
void PopBack(pList* pHead)//尾删法
{  
pLinkNode cur=*pHead;
   assert(pHead);
   //没有节点
   if(cur==NULL)
   {
   return;
   }
   //有一个节点
   else if(cur->next==NULL)
   {
   free(cur);
   cur=NULL;
   
   }
   //有两个或者两个以上的节点
   else
   {
   while(cur->next->next)
   {
   cur=cur->next;
   }
   free(cur->next);
   cur->next=NULL;
   }
}
//4 头插法建立单链表调用之前的BuyNode函数建立一个新的节点
//若当前链表为空则直接插入,否则将newNode->next=*pHead,再将*pHead=newNode
void PushFront(pList* pHead, DataType x)//头插法
{
pLinkNode newNode=BuyNode(x);
assert(pHead);
if(*pHead==NULL)
{
*pHead=newNode;
return;
}
else
{
      newNode->next=*pHead;
      *pHead=newNode;
}
}
//5 头删法删除单链表的节点
//(1)若单链表为空,则直接返回
/*(2)否则保存要删除的首节点,然后*pHead=(*pHead)->next;将要删除的节点释放即可*/
void PopFront(pList* pHead)//头删法
{   pLinkNode del;
assert(pHead);
if(*pHead==NULL)
{
return;
}
else
{
pLinkNode del=*pHead;//首先保存要删除的节点
*pHead=(*pHead)->next;
free(del);
    del=NULL;
}
}
//6 打印单链表
void PrintList(pList list)//打印单链表
{
while(list)
{
printf("%d->",list->data);
list=list->next;
}
printf("over");
}
//7 由于链表是在堆上动态开辟的存储空间,所以使用完之后必须销毁
//如果当前链表为空,则直接返回,反之开始判断当前节点是否为空,
//如果不为空,则保存并且释放掉,逐次向后遍历,即可达到销毁的目的
void Destroy(pList *pHead)
{
 pLinkNode cur=*pHead;
 assert(pHead);
 if(*pHead==NULL)
 {
   return;
 }
 else
 {
 while(cur)
 {
  pLinkNode del=cur;
  cur=cur->next;
  free(del);
  del=NULL;
 }
 
 }
}
//8 计算链表的长度
//设计一个计数器,一次遍历,只要当前节点不为空,给计数器自增即可
int GetListLength(pList head)
{
int count=0;
pLinkNode cur=head;
while(cur)
{
count++;
   cur=cur->next;
}
   return count;
}
//9 寻找链表中数据
//(1)首先判断如果当前链表为空,则查找失败直接返回
//(2)若当前节点不为空,则判断当前节点的数据域的值是否和要查找的数据相等,
//若相等则返回当前节点,否则逐次向后遍历,直到最后 任然没有找到则返回空即可
pLinkNode Find(pList head, DataType x)
{
pLinkNode cur=head;
if(cur==NULL)
{
return;
}
else
{
while(cur)
       if(cur->data==x)
   {
   return cur;
   }
   else
   cur=cur->next;
}
return NULL;
}
//10 在指定位置插入一个节点
//(1)断言要插入的位置是否合法
  /*(2)调用函数创建节点pLinkNode newNode=BuyNode(x);
(3)依次遍历当前节点和要插入的位置相等时newNode->next=cur->next,然后cur->next=newNode*/
void Insert(pList *pHead, pLinkNode pos, DataType x)
{
assert(pHead);
assert(pos);
pLinkNode newNode=BuyNode(x);
pLinkNode cur=*pHead;
while(cur!=pos)
{
cur=cur->next;
}
newNode->next=cur->next;
cur->next=newNode;
}
//11 删除链表中某个指定位置的节点
//(1)若当前链表为空则直接返回
//(2)判断当前节点是否为空,若不为空则判断是否和要删除的节点只相等
//(3)若相等,则判断是否为头节点,然后在删除
//(4)若相等且不为头节点,则有
void Remove(pList *pHead, DataType x)
{  
pLinkNode cur=*pHead;
pLinkNode del=NULL;
pLinkNode prev=NULL;
assert(pHead);
if(pHead==NULL)
{
return;
}
else
{
while(cur)
{
if(cur->data==x)
{
del=cur;
if(cur==*pHead)
{
*pHead=(*pHead)->next;
}
else
{
prev->next=cur->next;
}
free(del);
break;  //循环不能一味的执行下去,因为只删除一次
}
prev=cur;
cur=cur->next;
}
     }
}
void RemoveAll(pList *pHead, DataType x)
{  
pLinkNode cur=*pHead;
pLinkNode del=NULL;
pLinkNode prev=NULL;
assert(pHead);
if(pHead==NULL)
{
return;
}
else
{
while(cur)
{
if(cur->data==x)
{
del=cur;
if(cur==*pHead)
{
*pHead=(*pHead)->next;
cur=*pHead;
}
else
{
prev->next=cur->next;
cur=prev->next;
}
free(del);
}
else
{
prev=cur;
cur=cur->next;
}
}
     }
}
//链表常见的面试题
//1 删除非尾节点(删除无头单链表的非尾节点)
//(1)将当前节点和下一个节点的数据域交换,然后在删除下一个节点即可
void EraseNotTail(pLinkNode pos)
{
pLinkNode del=pos->next;//将要删除的节点保存起来,删除当前节点的下一个节点
pos->data=pos->next->data; /*交换当前即诶单和下一个节点的数据域*/
pos->next=del->next;
free(del);
del=NULL;
}
//2 反转链表  
//头插问题,每次取下一个节点依次头插,将第一个节点的指针域赋值为空,然后取下一个节点进行头插
void ReverseList(pList *pHead)
{
pLinkNode pnewHead=NULL;/*一注意赋空值定*/
pLinkNode cur=*pHead;
pLinkNode prev=NULL;
if(cur==NULL)
{
return;
}
if(cur->next==NULL)
{
return;
}
   while(cur)
   {
   prev=cur;
   cur=cur->next;
   prev->next=pnewHead;
   }
   *pHead=pnewHead;
}
//3 排序链表  (冒泡排序)
void Bubblesort(pList *pHead)
{
pLinkNode end=NULL;
pLinkNode cur=*pHead;
assert(pHead);
while(cur!=end)
{
while(cur&&(cur->next!=end))
{
if(cur->data>cur->next->data)
{
DataType tmp=cur->data;
cur->data=cur->next->data;
cur->next->data=tmp;
}
cur=cur->next;
}
end=cur;
cur=*pHead;
}
}
//4  在当前节点前插入一个数据
//首先在pos后插入一个数据,然后将pos和它后边的数据进行交换,完了删除后边的节点即可
void InsertFrontNode(pLinkNode pos,DataType x)
{
pLinkNode newNode=BuyNode(x);
assert(pos);//防止在空值后边插入一个数据
newNode->next=pos->next;
pos->next=newNode;
DataType tmp=pos->data;
pos->data=pos->next->data;
pos->next->data=tmp;
}
//5合并两个有序链表
pLinkNode Merge(pList l1,pList l2)
{
pLinkNode cur=NULL;
pList newHead=NULL;
if(l1==l2)
{
return l1;
}
if((l1==NULL)&&(l2!=NULL))
{
return l2;
}
if((l1!=NULL)&&(l2==NULL))
{
return l1;
}
if(l1->data<l2->data)//首先确定头结点
{
newHead=l1;
l1=l1->next;
}
else
{
newHead=l2;
l2=l2->next;
}
while(l1&&l2)
{
if(l1->data<l2->data)
{
cur->next=l1->next;
l1=l1->next;
}
else
{
cur->next=l1;
l2=l2->next;
}
cur=cur->next;
}
if(l1)
{  
cur->next=l1;
}
else
{
    cur->next=l2;
}
return newHead;
}
//其实有几道面试题没来得及整理呢
测试部分代码:
#define _CRT_SECURT_N0_WARNINGS 1
#include<stdio.h>
#include"linklist.h"
void Test1()
{
pList mylist;
InitLinkList(&mylist);
PushFront(&mylist,1);
PushFront(&mylist,2);
PushFront(&mylist,3);
PushFront(&mylist,4);
PrintList(mylist);
}
void Test2()
{
pList mylist;
InitLinkList(&mylist);
PushFront(&mylist,1);
PushFront(&mylist,2);
PushFront(&mylist,3);
PushFront(&mylist,4);
PrintList(mylist);
 
EraseNotTail(2);
PrintList(mylist);
}
void Test3()
{
pList l1;
InitLinkList(&l1);
PushFront(&l1,1);
PushFront(&l1,3);
PushFront(&l1,5);
PushFront(&l1,7);
PrintList(l1);
pList l2;
InitLinkList(&l2);
PushFront(&l2,2);
PushFront(&l2,4);
PushFront(&l2,6);
PushFront(&l2,8);
PrintList(l2);
pList l;
l=Merge(l1,l2);
PrintList(l);
}
int main()
{
void Test2();
system("pause");
}