[蓝蓝计算机考研算法训练二期]-day05

214 阅读4分钟

8、逆序输出链表。

输入一个链表的头节点,按链表从尾到头的顺序返回每个节点的值(用数组返回)。

如输入{1,2,3}的链表如下图:

img

返回一个数组为[3,2,1]

0 <= 链表长度 <= 10000

示例1

输入:

{1,2,3}

返回值:

[3,2,1]

示例2

输入:

{67,0,24,58}

返回值:

[58,24,0,67]

方法一

使用栈

思路

由于程序输入时的形式是中括号内包含数字的,所以先将输入的数据进行处理,changeChar方法中,第一个循环先利用数组,将中括号去掉,strtok() 函数将字符串按照逗号进行分割,atoi()再将分割出来的字符串转换为数字(后两个函数均为C标准库函数).接下来算法的处理方法就是对于声明的结构提,进行链表头节点和元素节点的创建,然后该方法使用栈的思想,顺序遍历链表的同时,将链表内的值入栈,当整个链表遍历完成之后,再进行出栈操作,同时,将所出栈的元素放入数组中遍历输出即可。

具体实现

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXN 10000

// 链表节点的结构体
struct ListNode {
    int val;
    struct ListNode *next;
};

// 栈的结构体
struct Stack {
    struct ListNode* arr[MAXN];
    int top;
};

// 初始化栈
void initStack(struct Stack *s) {
    s->top = -1;
}

// 入栈
void push(struct Stack *s, struct ListNode *node) {
    s->top++;
    s->arr[s->top] = node;
}

// 出栈
struct ListNode* pop(struct Stack *s) {
    if (s->top < 0) {
        return NULL;
    } else {
        struct ListNode *node = s->arr[s->top];
        s->top--;
        return node;
    }
}

// 计算链表长度
int getListLength(struct ListNode *head) {
    int len = 0;
    struct ListNode *p = head;
    while (p != NULL) {
        len++;
        p = p->next;
    }
    return len;
}

// 将链表节点值存入数组并返回
int* reversePrint(struct ListNode* head, int* returnSize){
    int len = getListLength(head);  // 计算链表长度
    int *res = (int*)malloc(sizeof(int) * len);  // 动态分配数组内存
    struct Stack s;
    initStack(&s);

    // 将链表节点入栈
    struct ListNode *p = head;
    while (p != NULL) {
        push(&s, p);
        p = p->next;
    }

    // 将栈中节点值存入数组
    int i = 0;
    while (s.top >= 0) {
        struct ListNode *node = pop(&s);
        res[i] = node->val;
        i++;
    }

    *returnSize = len;
    return res;
}

// 将输入的字符串转换为数组 
int changeChar(char *str, int *num) {
    int i,j=0;
    for (i = 0; str[i] != '}'; i++) {
        if (str[i] != '{') {
            str[j++] = str[i];
        }
    }
    str[j] = '\0';  // 确保新字符串以 '\0' 结尾
    // 分割字符串并转换为数字
    i = 0;
    char *token = strtok(str, ",");
    while (token != NULL) {
        num[i++] = atoi(token);
        token = strtok(NULL, ",");
    }
    return i;
}

// 测试
int main() {
    char str[MAXN*2+2];
    int num[MAXN];
    int i;
    scanf("%s", str);
    int len = changeChar(str, num);
	
    // 建立链表
    struct ListNode *head = NULL, *p, *q;
    for (i = 0; i < len; i++) {
    	p = (struct ListNode*)malloc(sizeof(struct ListNode));
        p->val = num[i];
        p->next = NULL;
        if (head == NULL) {
            head = p;
        } else {
            q->next = p;
        }
        q = p;
	}

    int returnSize = 0;
    int *res = reversePrint(head, &returnSize);

    // 输出结果
    printf("["); 
    for (i = 0; i < returnSize; i++) {
        printf("%d", res[i]);
        if(i != returnSize-1)
        	printf(",");
    }
    printf("]\n");

    // 释放内存
    free(head);
    free(p);
    free(q);
    free(res);

    return 0;
}

image.png image.png

方法二

使用递归

思路

大致内容与上述思路类似,只不过本方法的逆转使用的是递归的思想。

实现方法

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXN 10000

struct ListNode {
    int val;
    struct ListNode *next;
};

int getListLength(struct ListNode* head) {
    int len = 0;
    while (head != NULL) {
        len++;
        head = head->next;
    }
    return len;
}

void getNodeValues(struct ListNode* head, int* arr, int* index) {
    if (head == NULL) {
        return;
    }
    getNodeValues(head->next, arr, index);
    arr[(*index)++] = head->val;
}

int* reversePrint(struct ListNode* head, int* returnSize) {
    int len = getListLength(head);
    *returnSize = len;
    int* arr = (int*)malloc(sizeof(int) * len);
    int index = 0;
    getNodeValues(head, arr, &index);
    return arr;
}

// 将输入的字符串转换为数组 
int changeChar(char *str, int *num) {
    int i,j=0;
    for (i = 0; str[i] != '}'; i++) {
        if (str[i] != '{') {
            str[j++] = str[i];
        }
    }
    str[j] = '\0';  // 确保新字符串以 '\0' 结尾
    // 分割字符串并转换为数字
    i = 0;
    char *token = strtok(str, ",");
    while (token != NULL) {
        num[i++] = atoi(token);
        token = strtok(NULL, ",");
    }
    return i;
}

int main() {
    char str[MAXN*2+2];
    int num[MAXN];
    scanf("%s", str);
    
    int i;
    int len = changeChar(str, num);
	
    // 建立链表
    struct ListNode *head = NULL, *p, *q;
    for (i = 0; i < len; i++) {
    	p = (struct ListNode*)malloc(sizeof(struct ListNode));
        p->val = num[i];
        p->next = NULL;
        if (head == NULL)
            head = p;
        else
            q->next = p;
        q = p;
    }

	
    // 调用函数,返回数组 
    int returnSize;
    int* res = reversePrint(head, &returnSize);
    
    printf("["); 
    for (i = 0; i < returnSize; i++) {
        printf("%d", res[i]);
        if(i != returnSize-1)
        	printf(",");
    }
    printf("]\n");
    
    // 释放内存
    free(head);
    free(p);
    free(q);
    free(res);
    
    return 0;
}

image.png image.png

小结

今天算法的工作量确实大,按照题目所给的输入来计算确实有难度,而且为了使整个程序更容易理解整体思路,分开使用了两种方法。其实正常如果仅仅是逆序的话,不需要这么麻烦,可以将整个链表的元素顺序写入数组,一边写入一边计数,然后声明两个变量i和j,i从头开始,j从倒数位置开始两个位置交换,直到i>=j时停止循环。算是方法三吧很简便的一个方法实现逆序后数组输出。

算法部分代码如下,供大家参考

int* reversePrint(struct ListNode* head, int* returnSize) {
    int* res = (int*)malloc(sizeof(int) * MAXN);
    *returnSize = 0;
    struct ListNode* p = head;
    while (p != NULL) {
        res[(*returnSize)++] = p->val;
        p = p->next;
    }
    int i = 0, j = *returnSize - 1;
    while (i < j) {
        int temp = res[i];
        res[i] = res[j];
        res[j] = temp;
        i++, j--;
    }
    return res;
}