数据结构单链表

线性表的链式表示

特点:不需要使用地址连续的存储单元,插入删除直接修改指针,不需要移动元素

            缺点:失去随机存储的优点

1.单链表的节点定义:

//节点的定义
typedef struct LNode {
    int data;
    struct LNode* next;
} LNode, *LinkList;

一、头结点和头指针的区别:

头指针:永远指向链表的第一个节点

头结点:带有头结点的链表的第一个结点,结点通常不存储信息

二、引入头结点的优点:

1.由于第一个数据节点的位置被存储在头结点的指针域中,因此链表的第一个位置上操作和其他节点一致,不存在区别。

2.无论链表空还是非空,头指针都指向头结点的非空指针。空表非空表得到统一。

三、链表的判空条件

单链表:带头结点:L->next=NULL;  不带头结点:L=NULL;

双链表:带头结点:L->next=NULL;不带头结点:L=NULL;  

循环链表:带头结点:L->next=L;  不带头结点 L=NULL;

循环双链表:带头结点:L->next=L;L->prior=L;(L的前驱和后继都为自己) ;不带头结点L=NULL;

二、带有头结点的单链表基本操作

       1.头插法:直接定义新指针进行建立

                特点:顺序逆序

                时间复杂度 o(1) 若表长为n 时间复杂度 o(n)

LinkList headinsert(LinkList& L) {
    LNode* s;
    int X;
    L = (LNode*)malloc(sizeof(LNode));
    L->next = NULL;
    printf("请输入第1个数");
    scanf("%d", &X);
    int i = 1;
    while (X != 999999) {

        s = (LNode*)malloc(sizeof(LNode));
        s->data = X;
        s->next = L->next;
        L->next = s;
        i++;
        printf("输出第%d个数", i);
        scanf("%d", &X);

    }
    return L;

}

        2.尾插法建立链表:利用尾指针来建立链表

                特点:输入顺序于存储顺序一致

                时间复杂度 o(1) 插入n个:o(n)

LinkList tail(LinkList& L) {
    LNode* s;
    LNode* tail = L;
    int X;
    L = (LNode*)malloc(sizeof(LNode));
    int i = 0;
    printf("请输入%d个数", i);
    scanf("%d", &X);
    while (X != 999999) {
        s = (LNode*)malloc(sizeof(LNode));
        s->data = X;
        tail->next = s;
        tail = s;
    }
    tail->next = NULL;
    return L;
}

3.按序号查找节点(第i个数字结点)值:用变量j来遍历链表至空

   时间复杂度:O(n)

LNode* GetElem(LinkList& L, int i) {
    LNode* e = L->next;
    int j = 1;
    if (i == 0)
        return L;
    if (i < 1)
        return NULL;
    while (e != NULL && j < i) {
        e = e->next;
        j++;

    }
    return e;
}

4.按值查找结点 判断条件 p!=NULL&&p->data=NULL

   时间复杂度:O(n)

LNode* locatElem(LinkList& L, int X) {
    LNode* e = L->next;
    while (e != NULL && e->data != X)
        e = e->next;
    return e;
}

5.插入操作

        插入操作分为指定结点后插入和在第i个位置插入

        在第i个位置插入,首先用按位查找定位i-1个位置,然后插入到i-1指定结点后面

        时间复杂度:O(n)     

bool insertlist(LinkList& L, int i, int X) {
    LNode* s= (LNode*)malloc(sizeof(LNode));
    LNode* e = GetElem(L, i-1);
    s->data = X;
    s->next = e->next;
    e->next = s;
    return true;
}

      指定结点后插:

        时间复杂度O(1)

 LNode *s;
 s=(LNode *)malloc(sizeof(LNode));
 s->data=X;
 s->next=p->next;
 p->next=s;

 6.删除结点操作

        时间复杂度:O(n)用于寻找第i个结点

bool deletelist(LinkList& L, int i) {
    LNode* e = GetElem(L, i-1);
    LNode* p = e->next;
    e->next = p->next;
    free(p);
    return true;
}

        如为删除指定结点p,通过与后继结点交换数值,来删除后继结点,达到同样效果

   q=p->next;
   p->data=p->data;
   p->next=q->next;
   free(q);

7.求表长操作:

       从第一个数字结点开始用变量i=1来计数

int lengthlist(Linklist L){
    int i=0;
    while(L->next!=NULL){
        i=i++;
        L=L->next;
       
}
    return i;
}

 8.设计一个递归算法,删除不带头结点的单链表L中所有值为X的结点     

        思路:递归函数delx(L,x)    (画图理解)

                   1.递归出口 L=NULL;结点为空

                    2.递归: 若L.data[i]=X  删除结点 并继续递归delx(L->next,x)

                                   若L.data[i]!=X 递归delx(L->next,x);

bool delx(Linklist &L,x){
    if(L==NULL)
        return true;
    if(L->data==x){
        LNode *p;
        p=L;
        free(p);
        delx(L->next,x);
    }
    if(L->data!=x){
        delx(L->next,x);    
    }
    return true;
}

9.删除链表中X的值(带头结点)

思路一 删除X 利用一个前驱i结点删除值为x的结点(算个小断链)

            遍历链表若值为X,删除p,p后移,pre的后驱结点改为p,free掉原p结点;

            链表值若不为X,pre,p依次后移;

bool del_x_linklist(LinkList& L) {
    int val;
    cout << "请输入要删除的值" << endl;
    cin >> val;
    int k = 0;
    LNode* pre = L;
    LNode* q;
    LNode* p = pre->next;
    while (p != NULL) {
        if (p->data == val) {
            q = p;
            pre->next = p->next;
            p = pre->next;
            // p = p->next;
             //pre->next = p;
            free(q);
        }
        else
        {
            pre = p;
            p = p->next;
        }
    }
    return true;
}

思路二:记录值不为X的值用尾插法建立链表

如果值为X,不插入,跳过,值为X插入;

//尾插法建立
bool del_X2_LinkList(LinkList& L) {
    int val;
    cout << "请输入要删除的值" << endl;
    cin >> val;
    LNode* tail = L;
    LNode* s = L->next;
    LNode* q;
    while (s != NULL) {
        if (s->data != val)
        {

            tail->next = s;
            tail = s;
            s = s->next;
        }
        else {

            q = s;
            s = s->next;
            free(q);
        }
    }
    tail->next = NULL;
    return true;
}

 删除最小及节点(删除问题一般需要保留前驱结点,删除不断链)

思路:遍历整个链表找到最小值结点,设置结点有遍历的前驱pre,遍历结点p

           记录最小值结点min,最小值结点前驱

           然后删除此结点

bool delminlinklist(LinkList& L) {
    LNode* p = L->next;
    LNode* pre = L;
    LNode* minpre = L;
    LNode* min = L->next;
    while (p != NULL) {
        if (p->data < min->data) {
            min = p;
            minpre = pre;
        }

        pre = p;
        p = p->next;
    }
    minpre->next = min->next;
    free(min);
    return true;
}

类似删除最小值结点:

给定一个单链表,按次递增输出单链表各元素的值,并释放结点

思路:本题就是寻找最小结点并删除,然后遍历N次这个操作(删除需要前驱结点)

bool minupdel(LinkList &L){
    LNode *p=L->next;
    LNode *pre=L;
    LNode *minp=p;
    LNode *minpre=pre;
    while(L->next!=NULL)
    {    
        p=L->next;
        pre=L;
		while(p!=NULL){
           if(p->data<minp->data)
              {minp=p;
              minpre=pre;
            }
            pre=p;
            p=p->next;
                
    }
        minpre->next=minp->next;
		cout<<minp->data<<endl;
        free(minp);
    }
}
  

删除结点的值位于两个值之间的结点(删除问题,保留前驱结点)

思路:遍历链表,寻找满足条件的值,

        需要结点:前驱结点pre,遍历结点p

 //删除链表位于两值之间的值的节点
  bool dels_t(LinkList &L, int min, int max) {
            initlinklist(L);
            LNode* pre = L;
            LNode* p = L->next;
            while (p != NULL) {
                if (p->data > min && p->data < max) {
                    pre->next = p->next;
                    //free(p);
                    p = p->next;
                }
                else {
                    pre = p;
                    p = p->next;
                }
            }


            return true;
        }

10.链表逆置问题(断链)

       将结点p的后继结点指向前驱结点,并保证不断链设tail指针提前指向p的后继

        最后将头结点的指针指向p第一个结点(此时)

//逆置链表
//思路:断链操作
bool relinklist(LinkList& L) {
    LNode* pre, *p = L->next, *tail = p->next;
    p->next = NULL;  //第一个数据节点
    while (tail != NULL) {
        pre = p;
        p = tail;
        tail = p->next;
        p->next = pre;
    }
    L->next = p;
    return true;
}

11.反向输出每个节点的值(可以采用递归) 

void reprint(LinkList &L){
 
	if(L->next!=NULL)
		reprint(L->next);
	cout<<L->data<<endl;
  }

我的错误想法:

void reprint(LinkList &L){
    L=L->next;
    if(L!=NULL)
        reprint(L);
    if(L!=NULL)
        cout<<L->data<<endl;
}

 12.链表升序问题(其实也要断链,保留后继结点)

        思路:将链表一分为二,前部分初始只包括第一个数据结点,剩余链表属于另一部分

                    然后把后部分几点有序插入前部分 

bool sqlist(LinkList &L) {
    LNode *pre, *p = L->next;
    LNode *tail = p->next;  //tail设置来防止断链 
    p->next = NULL;
    p = tail;
    while (p!= NULL) {
        tail = p->next;  //tail设置来防止断链 
        pre = L;        //pre为已拍好序的链表头结点 
        while (pre->next!= NULL && pre->next->data < p->data) {
            pre = pre->next;
        }
        p->next = pre->next;
        pre->next = p;
        p = tail;
    }
    return true;
}

13.寻找两个链表的公共结点

     思路:两个链表的公共结点即某结点后两链表全部相同,这就要求链表同步遍历

                若链表不等长,需要先同步遍历处理,求表长,长链表优先遍历长度差

 LNode  *samelist(LinkList& A, LinkList& B) {
      int a = lengthlist(A);
      int b = lengthlist(B);
      int pos;
      LNode *longlist;
      LNode *shortlist;
      if (a > b) {
          longlist = A;
          shortlist = B;
          pos = a - b;
      }
      else {
          longlist = B;
          shortlist = A;
          pos = b - a;
      }
      longlist = longlist->next;
      shortlist = shortlist->next;
      while (pos--) {
          longlist = longlist->next;
      }
      while (longlist != NULL) {
          if (longlist->data == shortlist->data) {
              return longlist;
          }
          longlist = longlist->next;
          shortlist = shortlist->next;
      
      }
      return NULL;

  }
int lengthlist(LinkList &L) {
      int i = 0;
      LNode* p = L->next;
      while (p != NULL) {
          p = p->next;
          i++;
      }
      return i;
  }
int lengthlist(LinkList &L) {
      int i = 0;
      LNode* p = L->next;
      while (p != NULL) {
          p = p->next;
          i++;
      }
      return i;
  }

14.将带头结点的单链表分为两个单链表A和B,A含有原本单链表中奇数数据结点,B含有偶数结点,保持顺序不变

思路:用结点P来循环链表,奇数项保留在A,这就要求断链(P保证不断链)

                                                偶数项保留在B,新建链表都采用尾插法

LNode* dicreat_1(LinkList &A){
    int i=0; //判断奇数偶数
    LNode* B=(LNode*)malloc(sizeof(LNode));
    B->next=NULL;  //保留偶数
    LNode *p=A->next;
    A->next=NULL;  //保存奇数
    LNode *tailA=A;
    LNode *tailB=B;
    while(p!=NULL){
    i++;
    if(i%2==1){
       tailA->next=p;
       tailA=p; 
    }
    else
    {
        tailB->next=p;
        tailB=p;
    }
    p=p->next;
    }
    tailA->next=NULL;
    tailB->next=NULL;
    return B;
} 

 类似上题将链表A(a1,b1,a2,b2.......an,bn)输出为两个链表(a1,....an)和(bn....b1)

 bn采用头插法 注意P前插法断链 保留结点后驱!

LNode* dicreat_2(LinkList &A){
    int i=0; //判断奇数偶数
    LNode* B=(LNode*)malloc(sizeof(LNode));
    B->next=NULL;  //保留偶数
    LNode *p=A->next;
    A->next=NULL;  //保存奇数
    LNode *tailA=A;
    while(p!=NULL){
    i++;
    if(i%2==1){
       tailA->next=p;
       tailA=p;
	   p=p->next; 
    }
    else
    {
        LNode *q=p->next;
        p->next=B->next;
		B->next=p; 
    	p=q;
	}
    
    }
    tailA->next=NULL;
    return B;
} 

#include <iostream>
using namespace std;
#include <stdio.h>
#include <malloc.h>

//#define _CRT_SECURE_NO_WARNINGS
//队列
//单链表
//节点的定义
typedef struct LNode {
    int data;
    struct LNode* next;
}  *LinkList;

bool initlinklist(LinkList& L) {
    L = (LNode*)malloc(sizeof(LNode));
    if (L == NULL)
        return false;
    L->next = NULL;
    return true;

}

//头插法
LinkList headinsert(LinkList& L) {
    LNode* s;
    int X;
    int i = 1;
    L = (LNode*)malloc(sizeof(LNode));
    L->next = NULL;
    cout << "请输入第" << i << "个数" << endl;
    cin >> X;
    while (X != 999999) {

        s = (LNode*)malloc(sizeof(LNode));
        s->data = X;
        s->next = L->next;
        L->next = s;
        i++;
        cout << "请输入第" << i << "个数" << endl;
        cin >> X;

    }
    return L;

}

//尾插法
LinkList tail(LinkList& L) {
    LNode* s=(LNode*)malloc(sizeof(LNode));
    int X;
    L = (LNode*)malloc(sizeof(LNode));
    LNode* tail = L;
    int i = 1;
    cout << "请输入第" << i << "个数" << endl;
    cin >> X;
    while (X != 999999) {
        s = (LNode*)malloc(sizeof(LNode));
        s->data = X;
        tail->next = s;
        tail = s;
        i++;
        cout << "请输入第" << i << "个数" << endl;
        cin >> X;
        
    }
    tail->next = NULL;
    return L;
}

//按位查找
LNode* GetElem(LinkList& L, int i) {
    LNode* e = L->next;
    int j = 1;
    if (i == 0)
        return L;
    if (i < 1)
        return NULL;
    while (e != NULL && j < i) {
        e = e->next;
        j++;

    }
    return e;
}

LNode* locatElem(LinkList& L, int X) {
    LNode* e = L->next;
    while (e != NULL && e->data != X)
        e = e->next;
    return e;
}

bool insertlist(LinkList& L, int i, int X) {
    LNode* s= (LNode*)malloc(sizeof(LNode));
    LNode* e = GetElem(L, i-1);
    s->data = X;
    s->next = e->next;
    e->next = s;
    return true;
}

bool deletelist(LinkList& L, int i) {
    LNode* e = GetElem(L, i-1);
    LNode* p = e->next;
    e->next = p->next;
    free(p);
    return true;
}

bool printlinklist(LinkList& L) {
    LNode* p = L->next;
    while (p != NULL) {
        cout << p->data << endl;
        p =p ->next;

    }
    return true;
  }
//删除 链表中值为X的节点
bool del_x_linklist(LinkList& L) {
    int val;
    cout << "请输入要删除的值" << endl;
    cin >> val;
    int k = 0;
    LNode* pre = L;
    LNode* q;
    LNode* p = pre->next;
    while (p != NULL) {
        if (p->data == val) {
            q = p;
            pre->next = p->next;
            p = pre->next;
           // p = p->next;
            //pre->next = p;
            free(q);
        }
        else
        {
            pre = p;
            p = p->next;
        }
    }
    return true;
}

//尾插法建立
bool del_X2_LinkList(LinkList& L) {
    int val;
    cout << "请输入要删除的值" << endl;
    cin >> val;
    LNode* tail = L;
    LNode* s = L->next;
    LNode* q;
        while (s != NULL) {
            if (s->data != val)
            {

                tail->next = s;
                tail = s;
                s = s->next;
            }
            else {

                q = s;
                s = s->next;
                free(q);
            }
        }
    tail->next = NULL;
    return true;
    } 
//反向输出链表节点的值 
//思路:采用递归思想输出
void reprintlist(LinkList &L){
    LinkList p = L->next;
    if (p->next != NULL)
        reprintlist(p);
    if (p!= NULL)
            cout << p->data << endl;
    
}
//逆置链表
//思路:断链操作
bool relinklist(LinkList  &L) {
    LNode* pre, *p= L->next, *tail = p->next;
    p->next = NULL;  //第一个数据节点
    while (tail != NULL) {
        pre = p;
        p = tail;
        tail = p->next;
        p->next = pre;
    }
    L->next = p;
    return true;
}
//将一个带有头结点的单链表有序增加
bool sqlist(LinkList &L) {
    LNode *pre, *p = L->next;
    LNode *tail = p->next;
    p->next = NULL;
    p = tail;
    while (p!= NULL) {
        tail = p->next;
        pre = L;
        while (pre->next!= NULL && pre->next->data < p->data) {
            pre = pre->next;
        }
        p->next = pre->next;
        pre->next = p;
        p = tail;
    }
    return true;
}

//最小值删除
bool delminlinklist(LinkList& L) {
    LNode* p = L->next;
    LNode* pre = L;
    LNode* minpre = L;
    LNode* min = L->next;
    while (p != NULL) {
        if (p->data < min->data) {
            min = p;
            minpre = pre;
        }

        pre = p;
        p = p->next;
    }
    minpre->next = min->next;
    free(min);
    return true;
}
 //删除链表位于两值之间的值的节点
  bool dels_t(LinkList &L, int min, int max) {
            initlinklist(L);
            LNode* pre = L;
            LNode* p = L->next;
            while (p != NULL) {
                if (p->data > min && p->data < max) {
                    pre->next = p->next;
                    //free(p);
                    p = p->next;
                }
                else {
                    pre = p;
                    p = p->next;
                }
            }


            return true;
        }
  int lengthlist(LinkList &L) {
    
      int i = 0;
      LNode* p = L->next;
      while (p != NULL) {
          p = p->next;
          i++;
      }
      return i;
  }
  //寻找两个节点的公共节点
  LNode  *samelist(LinkList& A, LinkList& B) {
      int a = lengthlist(A);
      int b = lengthlist(B);
      int pos;
      LNode *longlist;
      LNode *shortlist;
      if (a > b) {
          longlist = A;
          shortlist = B;
          pos = a - b;
      }
      else {
          longlist = B;
          shortlist = A;
          pos = b - a;
      }
      longlist = longlist->next;
      shortlist = shortlist->next;
      while (pos--) {
          longlist = longlist->next;
      }
      while (longlist != NULL) {
          if (longlist->data == shortlist->data) {
              return longlist;
          }
          longlist = longlist->next;
          shortlist = shortlist->next;
      
      }
      return NULL;

  }

int main( ) {
    LinkList L;
    LinkList A, B;
   
    LNode *same;
    initlinklist(L);
    initlinklist(A);
    initlinklist(B);
    //initlinklist(Same);
    cout <<" 请输入A链表的值 "<< endl;
    tail(A);
    cout << "请输入B链表的数值 "<< endl;
    tail(B);
    //headinsert(L);
    //tail(L);
    //reprintlist(L);
    //insertlist(L, 1, 3);
    //printlinklist(L);
    //del_x_linklist(L);
    //relinklist(L);
    //sqlist(L);
    //delminlinklist(L);
    same=samelist(A, B);
    cout << same->data << endl;
    cout << "成功了" << endl;
    
    printlinklist(same);
    //dels_t(L, 10, 100);
    //printlinklist(L);
    //dels_t(L,10,100);
    //del_X2_LinkList(L);
    //printlinklist(L);
   //insertlist(L, 2, 4);
   //printlinklist(L);


        return 0;
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值