这是链表的第一篇。链表方便插入删除,但是不是随机存取。实际应用中,用于频繁的增加删除操作。
1.基础知识。
单链表:
<1>带头结点单链表
L为头指针,指向第一个结点,当有头结点时,第一个结点为头结点;当没有头结点时,第一个结点为存储第一个元素的a1结点
<2>不带头结点的单链表
两者区别:通常使用有头结点的链表,因为有头结点比较方便。因为对于类似头插法这样的操作,需要对第一个存储元素的结点进行操作时,很明显带有头结点的头指针可以保持不变,因为元素操作都在头结点与a1结点之间完成;而对于没有头结点的链表,需要频繁改变头指针的位置,才能保持头指针始终指向第一个结点。
2.基本操作
单链表模块
<1>头插法建立单链表:总是在头部操作,进行插值
代码:
typedef struct Node{ int data; struct Node *next; }Node; //头插法创建单链表,插入的元素总在头结点之后 Node *CreateList_L(Node* &L,int n){//前提L已经初始化完成 Node *p; int num;//p保存结点,num保存数据 L = (Node*)malloc(sizeof(Node));//创建头结点 L->next = NULL; for(int i=0;i<n;i++){ p = (Node*)malloc(sizeof(Node));//创建新的结点 cin>>num; p->data = num;//存入结点数据域 p->next = L->next;//p指向下一个 L->next = p;//再将p给单链表L的表头 } return L; //返回值为Node*型 }
<2>尾插法建立单链表:总是在尾部操作,进行插值
尾插法需要增加一个尾指针,尾指针总是用于指向最后一个尾结点
第一步:
第二步:
以此类推,每一次r都往后移动。
代码:
typedef struct Node{ int data; struct Node *next; }Node; //尾插法创建单链表,插入的元素总在最后一个位置 Node *CreateList_L(Node* &L,int n){//前提L已经初始化完成 Node *p,*r; int num;//p保存结点,num保存数据,r用于指向最后一个结点 L = (Node*)malloc(sizeof(Node));//创建头结点 L->next = NULL;
r = L;//初始时头尾指针指向同一个结点 for(int i=0;i<n;i++){ p = (Node*)malloc(sizeof(Node));//创建新的结点 cin>>num; p->data = num;//存入结点数据域 p->next = r->next;//p指向的下一个位置为NULL r->next = p;//r指针后移,指向尾结点
r = p;
} return L; //返回值为Node*型 }
<3>按序号查找结点值
//单链表结构 typedef struct LNODE { int data; struct LNODE *next; }node; node *FindById(Lnode *L,int id) { Lnode *p = L->next; int j=1; if(i==0) return L;//i=0返回头结点 if(i<1) return NULL;//i不合法,返回NULL: while(p!=NULL&&j<id) { p=p->next; j++; } return p;//返回第id个结点的指针,若id大于表长,则返回NULL; }
<4>按值查找
node *FindByValue(Lnode *L,int num) { Lnode *p = L->next; while(p!=NULL&&p->data!=num) { p=p->next; } return p;//找到则返回找到的结点的指针,否则返回NULL; }
插入结点操作:
1、p=FindById(L,id-1);//查找要插入位置的前驱结点 2、s->next=p->next;//s为要插入的结点 3、p->next=s;
删除结点操作:
1、p=FindById(L,i-1);//查找要删除位置的前驱结点 2、q=p->next;//令q指向要删除的结点 3、p->next=q->next;//将结点*q断开 4、free(q);//释放q内存
获取单链表长度
有头结点时:
int GetLength(Lnode *L) { Lnode *p = L->next; int i=0; while(p!=NULL&&p->data!=num) { p=p->next; i++; } return i;//返回长度; }
无头结点时:
int GetLength(Lnode *L) { Lnode *p = L;//与有头结点的区别 int i=0; while(p!=NULL&&p->data!=num) { p=p->next; i++; } return i;//返回长度; }
<5>单链表逆置
重复下面操作
代码
双链表模块
//双链表结构 typedef struct LNODE { int data; struct LNODE *prior,*next; }node;
1.双向链表的插入操作:
插入结点代码:
1、s->next=p->next;//将结点*s插入到*p之后 2、p->next->prior=s; 3、s->prior=p; 4、p->next=s;
语句顺序有多种,但是一定要保证第4步一定要1,2两句之后;但是我觉得上面语句最好记;因为和单链表的插入一样,都是先处理p->next指向的结点;处理完再处理其他的。
2.双链表的结点删除操作
双链表的删除比起单链表简单多了,因为单链表删除结点的时候,无法像双链表那样来回切换前后结点,因为双链表多了个prior指针域。
删除结点代码:
1、p->next=q->next;//将结点q链断开 2、q->next->prior=p; 3.free(q)
上面是通过两个结点信息,一个结点信息也可以删除。