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

229 阅读3分钟

24、设线性表L=(a1,a2,a3,...,an-2,an-1,an)采用带头结点的单链表保存,链表中的节点定义如下:

typedef struct node {
    int data;
    struct node* next;
}

请设计一个空间复杂度为O(1)且时间上尽可能高效的算法,重新排列题中的各个结点,得到线性表L=(a1,an,a2,an-1,a3,an-2,...)

要求:

1)给出算法的基本设计思想。

2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。

3)说明你所涉及的算法的时间复杂度。

思路

本题的难点在于,空间复杂度为O(1),其实实质上是链表的逆置,之后再进行插入,如何选择方法使链表逆置就成了一个问题,本题选用“速度双倍”的方法,p节点走一半,双倍速度的q节点就走到了末尾,将末尾链表进行逆置即可。

具体实现

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

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

// 创建链表的函数
struct ListNode* createList() {
    int val;
    printf("请输入链表元素的值(输入-1结束):\n");
    scanf("%d", &val);
    
    struct ListNode* head = NULL;
    struct ListNode* current = NULL;
    
    while (val != -1) {
        struct ListNode* node = (struct ListNode*)malloc(sizeof(struct ListNode));
        node->val = val;
        node->next = NULL;
        
        if (head == NULL) {
            head = node;
            current = node;
        } else {
            current->next = node;
            current = node;
        }
        
        scanf("%d", &val);
    }
    
    return head;
}

// 链表混合函数
struct ListNode* changeList(struct ListNode* head) {
	ListNode *p, *q, *r, *s;
	p=q=head;
	while(q->next != NULL) {		// 寻找中间节点 
		p = p->next;				// p走一步 
		q = q->next;
		if(q->next != NULL) 
			q = q->next;			// q走两步 
	}
	
	q = p->next;					// p所指节点为中间节点,q为后半段链表的首节点 
	p->next = NULL;
	while(q != NULL) {				// 将链表后半段逆置 
		r = q->next;
		q->next = p->next;
		p->next = q;
		q = r; 
	}
	
	s = head;						// s变成头节点 
	q = p->next;					// q是后半段的第一个数据点 
	p->next = NULL;
	while(q != NULL) {				// 将链表后半段插入指定位置 
		r = q->next;
		q->next = s->next;
		s->next = q;
		s = q->next;
		q = r; 
	}
	
	return head;
} 

// 打印链表的函数
void printList(struct ListNode* head) {
    struct ListNode* current = head;
    while (current != NULL) {
        printf("%d ", current->val);
        current = current->next;
    }
    printf("\n");
}

int main() {
	
    struct ListNode* list = createList();
    
    struct ListNode* result = changeList(list);
    
    printf("重新排列后的链表:"); 
    printList(result);
    
    return 0;
}

image.png

25、求元素间的距离。

定义三元组(a,b,c)(a、b、c均为正数)的距离D=|a-b|+|b-c|+|c-a|,给定3个非空整数集合S1、S2和S3,按升序分别存储在3个数组中。请设计一个尽可能高效的算法,计算并输出所有可能的三元组(a,b,c)(a∈S1,b∈S2,C∈S3)中的最小距离。例如S1={-1,0,9},S2={-25,-10,10,11},S3={2,9,17,30,41},则最小距离为2,响应的三元组为(9,10,9)。要求:

1)给出算法的基本设计思想。

2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。

3)说明你所涉及的算法的时间复杂度。

思路

本题初始使用一个足够大的整数来记录距离,由数学公式可推知,两个数距离的和其实就是最小值与最大值之间的距离的2倍,所以我们只需要固定一个值,假定为c,寻找与其最接近的a、b的值,将最大的和最小的相减即可得到最小的距离。

具体实现

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

#define MAX_LEN 10000
#define INT_MAX 0x7fffffff

// 计算绝对值
int absNum(int a) {
	if(a<0) 
		return -a;
	else 
		return a;
}

// a是否是三个数中的最小值
bool minNum(int a, int b, int c) {
	if(a < -b && a <= c) 
		return true;
	return false;
}

int createArr(int num[]) {
    char str[MAX_LEN];
    int n,i,j=0;
	
	// 将输入的内容按照字符串读入 
	scanf("%s", str);
	// 读取字符串中不含‘{’‘}’的内容 
	for (i = 0; str[i] != '}'; i++) {
        if (str[i] != '{') {
            str[j++] = str[i];
        }
    }
    
    // 读取要删除的数字 
    n = int(str[j+3]-'0');
    str[j] = '\0';  // 确保新字符串以 '\0' 结尾
    
	// 分割字符串并转换为数字
	i=0;
    char *token = strtok(str, ",");
    while (token != NULL) {
        num[i++] = atoi(token);
        token = strtok(NULL, ",");
    }
    
    return i;
}

// 计算三元组的最小距离 
int findMinofTrip(int A [],int n,int B [],int m,int C[], int p){
	int i=0, j=0, k=0, min=INT_MAX, d;
	while(i < n && j < m && k < p && min > 0){
		// 计算d 
		d = absNum(A[i] - B[j]) + absNum(B[j] - C[k] ) + absNum(C[k] - A[i]);
		if (d < min) 
			min = d; 	//更新 d
		if (minNum(A[i], B[j], C[k] ) ) 
			i++;
		else if(minNum(B[j], C[k], A[i]))
			j++;
		else 
			k++;
	}
	return min;
}

int main() {
	int a[10], b[10], c[10];
	printf("请输入三组数:\n");
	int n = createArr(a);
	int m = createArr(b);
	int p = createArr(c);
	
	int d = findMinofTrip(a, n, b, m, c, p);
	printf("最小距离是%d", d);
	
	return 0;
} 

image.png