链表的相关操作-数据结构与算法(北航991数据结构)

数据结构与算法(北航991数据结构)

下面的这一段代码是针对于这30道题而写的关于单向链表,双向链表以及循环链表等链表的创建及遍历,做辅助使用。
所有题目均是自己理解敲的,并执行成功的。可能会存在一起问题,或者有更优化的代码。欢迎留言讨论。谢谢

void INSERTLINK1(LinkList *list,ElemType item){ // 在链表首位插入新元素
    LinkList p  ;
    p = (LinkList) malloc(sizeof (LNode));
    p->data=item;
    p->link=*list;
    *list=p; // 是将list地址中的内容重新赋值为p的地址
}

//数组的遍历
void ARRAYPRINT(ElemType arr[],int n){
    printf("--------------------------------------\n");
    for (int i = 0; i < n; ++i) {
        printf("data:%d\n",arr[i]);
    }
    printf("--------------------------------------\n");
}

int LENGTH(LinkList list){
    LinkList p = list;
    int n = 0 ;
    while (p!=NULL){
        n++;
        p=p->link;
    }
    return n;
}
//遍历
void LINKPRINT(LinkList list){
    printf("================================\n");
    LinkList p = list;
    while (p != NULL){
        printf("data: %d\n",p->data);
        p=p->link;
    }
    printf("================================\n");
}

//建立一个线性链表
LinkList CREAT(int n){
    LinkList p , r , list=NULL;
    ElemType a;
    for (int i = 0; i < n ; ++i) {
        p=(LinkList) malloc(sizeof(LNode));
        a=i*30;
        p->data=a;
        p->link=NULL;
        if(list==NULL)
            list=p;
        else
            r->link=p;
        r=p;
    }
    return list;
}

//创建一个不带头结点的循环链表
LinkList CREAT2(int n){
    LinkList p , r , list =NULL;
    ElemType a;
    for (int i = 0; i < n ; ++i) {
        p=(LinkList) malloc(sizeof(LNode));
        a=i*30+15;
        p->data=a;
        p->link=NULL;
        if(list == NULL)
            list=p;
        else
            r->link=p;
        r=p;
    }
    r->link=list;
    return list;
}

LinkList CREAT3(int n){ //创建一个带头结点的循环链表
    LinkList p , r , list;
    list=(LinkList) malloc(sizeof (LNode));
    r = list;
    for (int i = 0; i < n ; ++i) {
        p = (LinkList) malloc(sizeof (LNode));
        p->data=(i+1)*15;
        r->link=p;
        r=p;
    }
    // 循环结束,r执行链表最后一个结点,再将r指向头结点
    r->link=list;
    return list;
}

DLinkList CREAT4(int n ){//创建不带头结点的双向循环链表
    DLinkList p , r , list = NULL;
    int a ;
    for (int i = 1; i <=n; ++i) {
        p=(DLinkList) malloc(sizeof (DNode));
        scanf("%d",&a);
        p->data=a;
        if(list == NULL)
            list=p;
        else{
            r->rlink=p;
            p->llink=r;
        }
        r=p;
    }
    r->rlink=list;
    list->llink=r;
    return list;
}


//创建带头结点的双向循环链表
DLinkList CREAT5(int n ){
    DLinkList p , r , list;
    list=(DLinkList) malloc(sizeof (DNode));
    list->rlink=NULL;
    list->llink=NULL;
    r=list;
    for (int i = 1; i <=n; ++i) {
        p=(DLinkList) malloc(sizeof (DNode));
        p->data=i*15-90;
        p->rlink = NULL;
        p->llink=NULL;

        r->rlink=p;
        p->llink = r;
        r=p;
    }
    p->rlink=list;
    list->llink=p;
    return list;
}

//创建带头结点的双向循环链表 .自己输入数
DLinkList CREAT6(int n ){//创建带头结点的双向循环链表 .自己输入data
    DLinkList p , r , list;
    list=(DLinkList) malloc(sizeof (DNode));
    list->rlink=NULL;
    list->llink=NULL;
    r=list;
    int a ;
    for (int i = 1; i <=n; ++i) {
        p=(DLinkList) malloc(sizeof (DNode));
        scanf("%d",&a);
        p->data=a;
        p->rlink = NULL;
        p->llink=NULL;

        r->rlink=p;
        p->llink = r;
        r=p;
    }
    p->rlink=list;
    list->llink=p;
    return list;
}

DLinkList2 CREAT7(int n ){//创建不带头结点的双向循环链表,针对第26题
    DLinkList2 p , r , list = NULL;
    int a ;
    for (int i = 1; i <=n; ++i) {
        p=(DLinkList2) malloc(sizeof (DNode2));
        scanf("%d",&a);
        p->data=a;
        p->freq=0;
        if(list == NULL)
            list=p;
        else{
            r->rlink=p;
            p->llink=r;
        }
        r=p;
    }
    r->rlink=list;
    list->llink=r;
    return list;
}



void LINKPRINT2(LinkList list){  // 遍历不带头结点的循环链表
    printf("==============不带头结点的循环链表==================\n");
    LinkList p = list;
    printf("data: %d\n",p->data);
    p=p->link;
    while (p != list){
        printf("data: %d\n",p->data);
        p=p->link;
    }
    printf("==============不带头结点的循环链表==================\n");
}

void LINKPRINT3(LinkList list){  // 遍历带头结点的循环链表
    printf("==============带头结点的循环链表==================\n");
    printf("头结点的data数据为:%d\n",list->data);
    LinkList p = list->link;
    while (p != list){
        printf("data: %d\n",p->data);
        p=p->link;
    }
    printf("==============带头结点的循环链表==================\n");
}

void DLINKPRINT4(DLinkList dlist){  //遍历不带头结点的双向循环链表
    printf("===============不带头结点的双向循环链表=================\n");
    printf("data: %d\n",dlist->data);
    DLinkList p = dlist->rlink;
    while (p != dlist){
        printf("data: %d\n",p->data);
        p=p->rlink;
    }
    printf("===============不带头结点的双向循环链表=================\n");
}

void DLINKPRINT5(DLinkList dlist){  //遍历带头结点的双向循环链表
    printf("===============双向循环链表=================\n");
    DLinkList p = dlist->rlink;
    while (p != dlist){
        printf("data: %d\n",p->data);
        p=p->rlink;
    }
    printf("===============双向循环链表=================\n");
}

void DLINKPRINT6(DLinkList2 dlist){  //链表双向循环链表 针对26题
    printf("===============双向循环链表=================\n");
    printf("data: %d,freq:%d\n",dlist->data,dlist->freq);
    DLinkList2 p = dlist->rlink;
    while (p != dlist){
        printf("data: %d,freq:%d\n",p->data,p->freq);
        p=p->rlink;
    }
    printf("===============双向循环链表=================\n");
}


int INSERTLINK5(LinkList list,int i , ElemType item){
    LinkList p , q ;
    int n = 1 ;
    q=list;
    while ( q != NULL && n <i){
        q=q->link;
        n++;
    }
    if( n != i || q==NULL){
        printf("链表不存在第i个链结点!");
        return -1;
    }
    p=(LinkList) malloc(sizeof (LNode));
    p->data=item;
    p->link=q->link;
    q->link=p;
    return 1;
}

1.已知长度为n的线性表A采用顺序存储结构。请写一算法,找出该线性表中值最小的数据元素,给出该元素在表中的位置。

//1.已知长度为n的线性表A采用顺序存储结构。请写一算法,找出该线性表中值最小的数据元素,给出该元素在表中的位置。
int LOCAL(ElemType arr[],int n){
    ElemType a;
    a=arr[0];
    int l = 0;
    for (int i = 1; i < n; ++i) {
        if(arr[i]<a) {
            a=arr[i];
            l=i;
        }
    }
    return l+1;
}

2.设计一个算法,用不多于3n/2的平均比较次数,在顺序表A[1…n]中分别找出最大值元素和最小值元素。

//2.设计一个算法,用不多于3n/2的平均比较次数,在顺序表A[1..n]中分别找出最大值元素和最小值元素。
int LOCALMAXANDMIN(ElemType arr[] , int n ){
    ElemType max,min;
    max=arr[0],min=arr[0];
    int count=0;
    for (int i = 1; i < n; ++i) {
        if(arr[i] > max) {
            max = arr[i];
            count++;
        }else if( arr[i] < min){
            min=arr[i];
            count +=2;
        } else count +=2;
    }
    return count;
}

3.已知长度为n的线性表A采用顺序存储结构,并假设表中每一个数据元素均为整型数据,请写出在该顺序表中查找值为item的数据元素的递归算法。

//3.已知长度为n的线性表A采用顺序存储结构,并假设表中每一个数据元素均为整型数据,请写出在该顺序表中查找值为item的数据元素的递归算法。
// 若查找成功,算法返回item在表中的位置,否则,算法返回信息-1。
int LOCAL3(int arr[] , int item, int l, int n ){
    if(l>=n) return -1;
    if(arr[l]==item) return l+1;
    LOCAL3(arr,item,l+1,n);
}

4.已知长度为n的线性表A采用顺序存储结构。请写出逆转该线性表的算法,

//4.已知长度为n的线性表A采用顺序存储结构。请写出逆转该线性表的算法,
// 即由A=(a,a2 ,…,an-1,a)产生A'=(an,an-1,…,a2,a1),要求在逆转过程中用最少的附加空间(用尽可能少的辅助变量)。
void INVERT4(ElemType arr[],int n){
    ElemType a;
    for (int i = 0; i <n/2; ++i) {
        a=arr[i];
        arr[i]=arr[n-i-1];
        arr[n-i-1]=a;
    }
}

5.已知长度为n的线性表A采用顺序存储结构,并且每个数据元素均为一个无符号整数,请写一算法,删除线性表中的所有奇数。

//5.已知长度为n的线性表A采用顺序存储结构,并且每个数据元素均为一个无符号整数,请写一算法,删除线性表中的所有奇数。
void DELETE5(unsigned int arr[],int *n){
    int d = -1;
    for (int i = 0; i < *n; ++i) {
        if(arr[i]%2 !=0)
            d++;
        else arr[i-d-1]=arr[i];
    }
    *n=*n-d-1;
}

6.已知长度为n的线性表A采用顺序存储结构。请写一时间复杂度为O(n)的算法,该算法删除线性表中原来序号为奇数的那些数据元素。

//6.已知长度为n的线性表A采用顺序存储结构。请写一时间复杂度为O(n)的算法,该算法删除线性表中原来序号为奇数的那些数据元素。
// ?这道题难道和上面那道题不一样吗?
void DELETE6(int arr[],int *n){
    int d = -1;
    for (int i = 0; i < *n; ++i) {
        if(arr[i]%2 !=0)
            d++;
        else arr[i-d-1]=arr[i];
    }
    *n=*n-d-1;
}

7.已知长度为n且按值有序排列的线性表A采用顺序存储结构。请写一算法,删除所有值大于x且小于y的数据元素。

//7.已知长度为n且按值有序排列的线性表A采用顺序存储结构。请写一算法,删除所有值大于x且小于y的数据元素。
void DELETE7(int arr[],int *n , int x ,int y){
    int d = -1;
    for (int i = 0; i < *n; ++i) {
        if(arr[i]>x && arr[i]<y)
            d++;
        else
            arr[i-d-1]=arr[i];
    }
    *n=*n-d-1;
}

8.请写一算法,通过键盘输人一系列数据元素,建立一个长度为n且不包含重复元素的线性表A。这里,设线性表A采用的存储结构为顺序存储结构,并且假设空间足够。

//8.请写一算法,通过键盘输人一系列数据元素,建立一个长度为n且不包含重复元素的线性表A。这里,设线性表A采用的存储结构为顺序存储结构,并且假设空间足够。
void CREATE8( int A[],int n ){
    int i = 0;
    int a ;
    while (i<n){
        scanf("%d",&a);
        int j ;
        for (j = 0; j < i ; ++j) {
            if(A[j]==a)
                break;
        }
        if(i==0 || (j==i && A[j] != a)){
            A[i++]=a;
        }
    }
}

9.已知线性表A与线性表B的长度分别为n与m,并且都采用顺序存储结构。请写一算法,在线性表A的第i个位置插入线性表B。约定:不考虑存储空间溢出问题。

//9.已知线性表A与线性表B的长度分别为n与m,并且都采用顺序存储结构。请写一算法,在线性表A的第i个位置插入线性表B。约定:不考虑存储空间溢出问题。
void INSERT9(ElemType arr1[],int *n, int i ,int m ,ElemType arr2[]){ // 这里暂时考虑 i 小于等于n
    int j;
    for (j= i; j < *n+1 ; ++j) {
        arr1[j+m-1]=arr1[j-1];
    }
    for (j=i;j<m+i;j++){
        arr1[j-1]=arr2[j-i];
    }
    *n=*n+m;
}

10.已知线性链表第1个结点的存储地址为list。请写一算法,把该链表中数据域值为d的所有结点的数据域值修改为item。

//10.已知线性链表第1个结点的存储地址为list。请写一算法,把该链表中数据域值为d的所有结点的数据域值修改为item。
void ALTER10(LinkList list, ElemType d ,ElemType item){
    LinkList p = list;
    while ( p != NULL){
        if( p->data == d )
            p->data=item;
        p=p->link;
    }
}

11.已知非空线性链表的第1个结点的指针为list,请写出删除该链表第i个结点的算法。

//11.已知非空线性链表的第1个结点的指针为list,请写出删除该链表第i个结点的算法。
void DELETE11(LinkList *list,int i){
    if(i==1){
        *list=(*list)->link;
    } else{
        LinkList r = *list;
        LinkList p = r->link;
        int count = 2;
        while ( p != NULL && count != i){
            r=p;
            p=p->link;
            count++;
        }
        if( p!=NULL && count == i){
            r->link=p->link;
            free(p);
        }
    }
}

12.已知非空线性链表第1个结点的存储地址为list,请写出删除链表中从第i个结点开始的(包括第i个结点本身)连续k个结点的算法。

//12.已知非空线性链表第1个结点的存储地址为list,请写出删除链表中从第i个结点开始的(包括第i个结点本身)连续k个结点的算法。
void DELETE12(LinkList *list,int i , int k ){ // *list 的原因是方式i是从第一个节点开始删除
    int count = 1;
    LinkList r , p= *list ;
    while ( p != NULL && count != i+k-1){ // 外层循环控制p到达要删的最后一个节点,即第i+k-1个节点
        while (count < i){   //内循环使p到达要删除的第一个节点,r是p的前驱节点
            r=p;
            p=p->link;
            count++;
        }
        p=p->link;
        count++;
    }
    if(p==NULL){
        printf("已超链表长度");
    } else if(i==1)
        *list = p->link;
    else
        r->link= p->link;
}

13.已知线性链表第1个结点指针为list。请写一算法﹐删除链表中数据域值最大的结点。

//13.已知线性链表第1个结点指针为list。请写一算法﹐删除链表中数据域值最大的结点。
void DELETE13(LinkList *list) {  // *list 防止第一个位置是最大的节点 , 此算法如果最大值多个,也只能删掉一个
    LinkList p = *list , q = *list, r ,s  ; // q用来保存数值域最大的那个结点,初始值为第一个节点,r为q的前驱节点,s为p的前驱节点
    while ( p != NULL){
        if( p->data > q->data) // 如果当前节点比最大结点大,则将当前节点赋给最大节点。
        {
            r = s; //设置s的目的是为了,在保存最大值节点的同时,能保存最大值的前驱节点,设置r的目的是为了便于单链表删除q最大值节点
            q = p;
        }
        s = p ;
        p = p->link;
    }
    if( q == *list ) // 如果最大节点是第一个节点
        *list=(*list)->link;
    else{
        r->link=q->link;
    }
}

14.已知线性链表第1个结点指针为list。请写一算法,判断该链表是否是有序链表(结点是否按照数据域值的大小链接),若是,算法返回1,否则,算法返回0。

//14.已知线性链表第1个结点指针为list。请写一算法,判断该链表是否是有序链表(结点是否按照数据域值的大小链接),若是,算法返回1,否则,算法返回0。
int ISORDER14(LinkList list){ // 此算法中设置两个int变量,来辅助判断是否有序
    int a =1 , b =1;
    LinkList p=list , q=list->link ; //p为q的前驱结点
    while (q != NULL) {
        a = b ; // a保存的是前两个数的差
        // b保存的是当前两个数的差,如果前后两个数一样,差为0,则b的值赋1,防止下面0乘任何数都是0
        b = (q->data - p->data) ?  (q->data - p->data): 1 ;
        if(a*b < 0 ) return 0; // 如果异号,则不是有序链表
        p = q ;
        q=p->link;
    }
    return 1;
}

15.已知线性链表第1个链结点指针为list。请写一算法,交换p所指结点与其下一个结点的位置(假设p指向的不是链表中最后那个结点)。

void SWAP15(LinkList *list , LinkList p){ //*list 防止p是第一个结点
    LinkList l , r=*list , q=(*list)->link ; // r是为了找到p结点的前驱节点 ,q遍历
    if(p == *list){
        r->link=q->link;
        q->link=r;
        *list=q;
    } else {
        while (q != NULL && q != p) {
            r = q;
            q = q->link;
        }
        if( q != NULL){   // 假设此时的顺序是 r-q-f-g
            r->link=q->link;  // 将r指向q的下一个节点 r-f-g , q-f-g
            q->link=q->link->link; // q指向q的节点的下一个节点 r-f-g,q-g
            r->link->link=q; // 将r的下一个节点指向q   r-f-q-g 完成互换
        }
    }
}

16.已知非空线性链表第1个结点由 list 指出。请写一算法,将链表中数据域值最小的链结点移到链表最前面。

//16.已知非空线性链表第1个结点由 list 指出。请写一算法,将链表中数据域值最小的链结点移到链表最前面。
void SWAPMIN16(LinkList *list){ //先找到最小的节点
    LinkList q , r , p , l ; // q 保存最小的节点,r保存q的前驱节点,p遍历节点,l保存的是p的前驱节点
    p=*list,q = *list; // q的初始节点是第一个节点
    while ( p != NULL){ //查找最小的节点
        if(p->data < q->data){
            r=l;
            q=p;
        }
        l=p;
        p=p->link;
    }
    if( q != *list ){
        r->link=q->link;
        q->link=(*list);
        *list=q;
    }
}

17.(2009年硕士研究生入学考试计算机专业基础综合全国联考试题)
请写一算法,该算法用尽可能高的时间效率找到由list所指的线性链表的倒数第k个结点。 若找到这样的结点,算法给出该结点的地址;否则,算法给出信息NULL。
限制:算法中不得求出链表的长度,也不允许使用除指针变量和控制变量以外的其他辅助空间。

/*
17.(2009年硕士研究生入学考试计算机专业基础综合全国联考试题)
请写一算法,该算法用尽可能高的时间效率找到由list所指的线性链表的倒数第k个结点。 若找到这样的结点,算法给出该结点的地址;否则,算法给出信息NULL。
限制:算法中不得求出链表的长度,也不允许使用除指针变量和控制变量以外的其他辅助空间。
*/
//初步想法是递归,递归是其他的辅助空间吗?其次是设置一个count计数;
LinkList RECIPROCAL17(LinkList list , int k){
    LinkList p , r ;
    int count = 0 ; //count 代表r和p相差的结点数
    p=list,r=list;
    while (p != NULL){
        if(count >= k )  //设置count计数的目的是让r和p始终悬殊k个节点,当p等于NULL时,r就是倒数第k个节点
            r=r->link;
        count++;
        p=p->link;
    }
    if(count >= k)
        return r;
    else
        return NULL;
}

18.设线性表X=(x1,x2,x3,…,xn)与Y=(y1,y2,y3,…,ym)都采用链式存储结构(链表第1个结点的指针不妨分别用X和Y表示)。试写一个算法合并这两个线性链表为一个线性链表,使得 X 和 Y 每个元素交叉排列

//18.设线性表X=(x1,x2,x3,…,xn)与Y=(y1,y2,y3,…,ym)都采用链式存储结构(链表第1个结点的指针不妨分别用X和Y表示)。
// 试写一个算法合并这两个线性链表为一个线性链表,使得 X 和 Y 每个元素交叉排列
LinkList MERGE18(LinkList list1,LinkList list2){
    LinkList p=list1 , q=list1->link ,r=list2 ; // 初始p指向list1的第一个节点
    int count = 0 ;
    while (q != NULL && r != NULL){  // 两个链表都没有遍历完
        if(count %2 ==0){  // count的计数器是为了两个链表轮流差入
            p->link=r;
            p=r;
            r=r->link;
        } else{
            p->link=q;
            p=q;
            q=q->link;
        }
        count++;
    }
     p->link = q ? q : r; //最后把非空剩余的链表插入
    return list1;
}

19.已知线性链表第1个结点的指针为 list。请写一算法,删除数据域值相同的多余结点,即若链表中有多个结点具有相同的数据域值,只保留其中一个结点,其余结点均从链表的最后删去﹐使得到的链表中所有结点的数据域值都不相同。

/*
 * 19.已知线性链表第1个结点的指针为 list。请写一算法,删除数据域值相同的多余结点,即若链表中有多个结点具有相同的数据域值,
只保留其中一个结点,其余结点均从链表的最后删去﹐使得到的链表中所有结点的数据域值都不相同。
 */
void DISTINCT19(LinkList list){
    LinkList p=list->link , q=list, r=list  ;
    int flag = 1 ;
    while ( p!=NULL ){  // 外层的循环是控制每个节点都和   此节点之前的数据进行比较(即内层循环的那些节点)
        while (q != p){ // 内存的循环是依次遍历首结点到p结点之间有无相等数据
            if( p->data == q->data){
                r->link=p->link;
                flag = 0 ; // 设置flag的目的是判断下次循环p是从哪个节点开始
                free(p); //释放
                break; // 重复元素都已经删除了,就没必要再往下循环了,跳出内循环
            }
            q=q->link;
        }
        q=list;  //重置内层的q的节点为起始结点
        if(!flag){ //如果删除元素了,则r的节点不变,p的节点是r的下一个节点
            p=r->link;
        } else{ //如果没删元素,则r后移,p后移,r是p的前驱节点
            r=p;
            p=p->link;
        }
        flag=1; // 重置flag ,开始下一次循环
    }
}

20.请写一算法,依次输出通过键盘输人的一组整型数据中的最后k个元素。约定;以Ctrl+z作为键盘输入的结束, 并假设k≤输入的数据元素的个数。 限制;算法中不允许使用数组,也不允许有计算输入数据个数的过程。

21.已知一个不带头结点也无头指针变量,并且长度大于1的循环链表。请写一算法﹐删除p所指链结点的直接前驱结点。

//21.已知一个不带头结点也无头指针变量,并且长度大于1的循环链表。请写一算法﹐删除p所指链结点的直接前驱结点。
void DELETE21(LinkList p){
    LinkList  q , r ;
    q = p ;
    while ( q->link != p){ // 遍历循环链表,找到p结点的直接前驱节点,魂环结束后q节点就是p的直接前驱节点。
        r=q;  // 要删除p的前驱节点,除了找到p节点的直接前驱节点外,还要知道p节点的直接前驱节点的前驱节点r
        q=q->link;
    }
    r->link=p;
}

22.请写出将一个线性链表(第1个结点的存储地址为list)分解为两个循环链表,并将两个循环链表的长度存放在各自的头结点的数据域中的算法。
分解规则:若线性链表中某一链结点属于第1个循环链表,则下一个链结点就属于第2个循环链表;反之,若线性链表中某一个链结点属于第⒉个循环链表,则下一个链结点就属于第1个循环链表。

/*22.请写出将一个线性链表(第1个结点的存储地址为list)分解为两个循环链表,并将两个循环链表的长度存放在各自的头结点的数据域中的算法。
分解规则:若线性链表中某一链结点属于第1个循环链表,则下一个链结点就属于第2个循环链表;
反之,若线性链表中某一个链结点属于第⒉个循环链表,则下一个链结点就属于第1个循环链表。*/
void SPLIT22(LinkList list , LinkList list1 ,LinkList list2){  // 这里,先考虑把分成的两个魂环链表当做参数传入的方式
    LinkList p = list , q = list1 , r = list2;
    int count1 = 0 ,count2=0; //count1表示list的链表总数,和count2分别表示第二个链表的个数,count1辅助判断结点该插往哪个链表中
    while (p!=NULL){
        if(count1 % 2 == 0){
            q->link=p;
            q=p;
        } else{
            r->link=p;
            r=p;
            count2++;
        }
        count1++;
        p=p->link;
    }
    //最后分别将两个循环链表的最后一个节点指向头结点,并将链表长度写入头结点的data中;
    q->link=list1;
    r->link=list2;
    list1->data=(count1-count2);
    list2->data=count2;
}

23.已知带头结点的循环链表的头结点指针为list,请编写一个逆转链表链接方向的算法。

//23.已知带头结点的循环链表的头结点指针为list,请编写一个逆转链表链接方向的算法。
void INVERT23(LinkList list){ // 逆转循环链表的思路与逆转单向链表的思路一样
    LinkList p , r , q = list;  // q初始值为list,这样就保证了最后一个节点指向头结点,
    p=list->link;
    while (p != list){
        r=p;
        p=p->link;
        r->link=q;
        q=r;
    }
    // 循环结束后不包括头结点的链表逆转结束。再将逆转后的链表q链表到链表上
    list->link=q;
}

24.已知非空线性表(a1,a2,…,an-1,an)采用仅设置了末尾结点指针的单向循环链表作为存储结构(设末尾结点指针为rear), 请写一算法,将线性表改造为(a1,a2,…,an-1,an,an-1,…,a2,a1)。 要求:改造后的线性表依然采用仅设置末尾结点指针的单向循环链表存储,并且算法中只能出现一个循环。

/*
 * 24.已知非空线性表(a1,a2,…,an-1,an)采用仅设置了末尾结点指针的单向循环链表作为存储结构(设末尾结点指针为rear),
 * 请写一算法,将线性表改造为(a1,a2,…,an-1,an,an-1,…,a2,a1)。
 * 要求:改造后的线性表依然采用仅设置末尾结点指针的单向循环链表存储,并且算法中只能出现一个循环。
 * */
//单向循环链表的尾节点和头结点貌似没什么区别,因为是单向循环
// 因为是单向链表,所以等于将链表进行扩充,这种解题思路可以理解成新增一个链表,使其是原始链表的逆转链表,最后两个链表再连接到一起就可以了
void ALTER24(LinkList rear){
    LinkList p ,  q ,r,list=NULL ; //list是一个新的链表
    p=rear->link;
    while (p->link!= rear ){ //循环遍历的同时将当前节点加在list节点的首端,当p是最后一个节点时,退出循环
        q=(LinkList) malloc(sizeof (LNode));
        q->data = p->data;
        if(list == NULL)
            list=q;
        else
            q->link=r;
        r=q;
        p=p->link;
    }
    p->link=r; //此时p是最后一个节点,r是list节点的头结点,将两个链表相连
    list->link=rear; //list节点指向原本节点的尾节点,两个节点连成一个新的单向循环链表
}

26.已知不带头结点的双向链表第1个结点的指针为list,链结点中除了数据域和分别指向该结点直接前驱结点和直接后继结点的指针域外, 还设置了记录该结点被访问的次数的频度域freq(初始值为0)。请设计一个算法LOCATE(list,x), 该算法的功能是每当在此链表上进行一次LOCATE(list,x)操作,数据信息为x的结点的freq域的值增1,* 并且保持链表中链结点按freq域值递减链接,以使得频繁被访问的链结点靠近链表的前端。

//这道理的解题思路是每访问一次数据域为x的结点后,将freq加1,并将这个结点重新排序
//本题没有说明,这个结点数据域全不相等,因此下面的解题是按照数据域有可能有多个一样并且初始的排列顺序比较散
void LOCATE26(DLinkList2 *list , ElemType item){ //*list的原因是有可能头结点被替换
    DLinkList2 p = *list , q , r = *list,w ;
    if(p->data == item) //如果是访问的数据域和头结点一样,则头结点的freq加1
        (p->freq)++;
    p=p->rlink; // 指向下一个节点,遍历整个链表,看是否还有数据域为item的节点
    while ( p != r) {  //r始终是头结点位置,即使头结点被刷新,当p再次指向头结点时,循环遍历结束
        q = p->llink;  
        w = p->rlink;
        if (p->data == item) { //把链表中所有data值为item的都刷新freq
            (p->freq)++;
            if (p->freq > q->freq) {  // p的freq值刷新后如果大于前面节点,就进入下面的代码,否则不进入后移p遍历下一个结点
                q->rlink = p->rlink; //p的freq的值大于前面的节点,则将p从当前连接中删除,即p的上一个结点和下一个结点相连
                p->rlink->llink = q; // 删除p的原因是,此时这个位置已经不适合p待了,得搬家
                while (p->freq > q->freq && q != r) { //这个循环的目的是使p找到合适的插入位置
                    q = q->llink; // q不断的前移,直到移动到p的freq不再大于q的freq,或者是移动到头结点
                }
                //上面循环结束之后,需要先判断p的结点的freq是否大于头结点的freq,如果大于就说明上面的while循环是q到达头结点而退出的
                if (p->freq > r->freq) {  //将p插入到头结点前
                        r->llink->rlink = p; 
                        p->llink = r->llink;
                        r->llink = p;
                        p->rlink = r;
                        r = p;
                        *list = r;  //此时头结点已经不是之前的头结点了,需要更换头结点的地址
                } else { //进入这个条件说明上面while循环结束的循环是p的freq不再大于q的freq,即p待插入的地址是q的后面
                    q->rlink->llink = p;
                    p->rlink = q->rlink;
                    q->rlink = p;
                    p->llink = q;
                }
            }
        }
        p=w; //w的作用是每次执行过后,p节点都能重新的指向原本链表的下一个节点。所以设置w至关重要
    }
}

27.已知带有头结点的双向循环链表中头结点的指针为list,请写出删除并释放数据域内容为x的所有结点的算法。

/*
 * 27.已知带有头结点的双向循环链表中头结点的指针为list,请写出删除并释放数据域内容为x的所有结点的算法。
 */
void DELETE27(DLinkList list,ElemType x){
    DLinkList p = list->rlink,r;
    while (p != list){
        r=p;// r的作用是当删除数据域为x的节点并释放后,p能再次的指向删除节点的下一个节点
        if(p->data == x){
            p->llink->rlink=p->rlink;
            p->rlink->llink=p->llink;
            free(p);
        }
        p=r->rlink;
    }
}

28.已知不带头结点的双向循环链表第1个结点指针为list,请写一算法﹐判断该链表是否为对称链表, 即前后对应结点的数据信息相同。对称返回1,否则返回0。

/*
 * 28.已知不带头结点的双向循环链表第1个结点指针为list,请写一算法﹐判断该链表是否为对称链表,
 * 即前后对应结点的数据信息相同。对称返回1,否则返回0。
 * */
int symmetry28(DLinkList list){
    DLinkList p=list,q=list->llink;  // p从右边遍历,q从左边遍历
    while (p!=q && p->rlink != q){ // 当链表为奇时,p和q会在同一个节点相遇,即p==q时退出循环,当链表为偶相遇时,p的下一个右节点等于q或者q的下一个左节点等于左。
        if(p->data != q->data ) return 0; //如果碰到 p != q的返回0;
        p=p->rlink;
        q=q->llink;
    }
    return 1; //顺利执行完,则表示都相等,返回1
}

29.请写一算法,该算法的功能是先通过键盘输入n个整型数据,建立一个带有头结点的双向循环链表,然后按照与输入相反的次序依次输出这n个整型数据。

/*29.请写一算法,该算法的功能是先通过键盘输入n个整型数据,建立一个带有头结点的双向循环链表,
 * 然后按照与输入相反的次序依次输出这n个整型数据。
 * */

void SCANFPRINTF29(int n){// 这道题简单,先从头结点的右节点,以此建立链表,再从头结点的左侧开始以此遍历左节点
    DLinkList dlist, p ,q ;
    dlist=(DLinkList) malloc(sizeof (DNode));
    p=dlist;
    int a ;
    for (int i = 0; i < n; ++i) {
        q=(DLinkList) malloc(sizeof (DNode));
        scanf("%d",&a);
        q->data=a;
        p->rlink=q;
        q->llink=p;
        p=p->rlink;
    }
    p->rlink=dlist;
    dlist->llink=p;

    printf("相反的次序输出\n");
    int i = 0 ;
    while (p != dlist){
        printf("data%d:%d\n",++i,p->data);
        p=p->llink;
    }
}

30.已知带头结点的双向循环链表头结点指针为list,除头结点外的每个链结点数据域值为一个整数。请写一算法,将链表中所有数据域值大于0的结点放在所有数据域值小于0的结点之前。 若链表中除头结点外没有其他结点,算法返回0,否则,算法返回1。

int   ALTER30(DLinkList Dlist){
    DLinkList r = Dlist, q=Dlist,l ,  p = Dlist->rlink; //
    if(p == Dlist) return 0;  // 若链表中除头结点外没有其他结点,算法返回0
    while ( p != Dlist){ //循环遍历这个链表的每个结点进行比较
        if(p->data > 0){  // 如果当前节点大于0就放在头结点后面
            r->rlink=p;
            p->llink=r;
            p=p->rlink; // p节点后移,使遍历全部结点
            r=r->rlink; // r后移,便于下一个大于0的节点插在后面
        } else {  //如果当前结点不大于0就放在头结点前面(所谓头结点前面,就是这个循环链表相对于头结点的末尾结点)
            q->llink=p; 
            l = p;  // l的作用是保存p的结点,因为下一步p的节点将后移
            p=p->rlink; //这一步必须放在下一步前面,否则原来p节点的下一节点将被覆盖,使其遍历的下一节点不是原本链表的下一节点
            l->rlink=q; //l的左右就是当p节点后移之后,将当前节点连接到左端
            q=l; //q=l,使q一直保持头结点的最前面待插入的结点
        }
    }
    r->rlink=q; //最后将相对于头结点左侧的结点和右侧的结点相连使其再次成为完整的循环链表
    q->llink=r;
    return 1;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值