1、数组
1.1、基本原理
在C语言中,数组是一种线性数据结构,由相同数据类型的元素按一定顺序排列而成。它们在内存中被分配成一个连续的块,并通过下标来访问各个元素。
数组可以用一维、二维或多维方式定义。一维数组由一个有限元素序列组成,可以通过索引访问其中的元素;二维数组由多个一维数组组成,每个一维数组表示二维数组中的一行或一列;多维数组则是由多个二维数组或一维数组组成的数据结构。
在C语言中,可以使用以下语法来定义一个数组:
datatype array_name[array_size];
其中,datatype 表示数组中元素的数据类型,array_name 是数组的名称,array_size 表示数组中元素的数量。
例如,下面是一个包含5个整数的一维数组的定义:
int numbers[5];
可以使用下标来访问数组中的元素,下标从0开始。例如,访问数组中第一个元素的语法如下:
numbers[0] = 10;
这将把值10存储在数组的第一个元素中。同样,可以使用循环来遍历整个数组并访问其中的每个元素。
1.2、实现
数组的实现依赖于底层的内存分配和访问方式。由于数组中的元素在内存中是连续存储的,因此可以通过指针对数组进行操作。数组名是数组第一个元素的地址,因此可以使用指针来访问数组中的元素。
以下是一个简单的数组实现示例,展示了如何创建数组、对数组进行赋值和访问
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5}; // 创建一个大小为5的整型数组并初始化
int i;
for (i = 0; i < 5; i++) {
printf("arr[%d] = %d\n", i, arr[i]); // 访问数组元素并打印
}
return 0;
}
输出:
arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[3] = 4
arr[4] = 5
以上代码创建了一个大小为5的整型数组,并初始化为1到5的连续整数。然后通过for循环依次访问数组的每个元素并打印输出。
2、链表
2.1、基本原理
链表是一种线性数据结构,它由若干个节点构成,每个节点包含两个部分:数据域和指针域。数据域用于存储节点的数据,指针域用于指向下一个节点。链表中的节点不一定是连续存储的,它们通过指针连接在一起,因此链表具有动态性和灵活性。链表的特点是可以快速插入和删除元素,但查找操作需要遍历整个链表。
2.2、实现
链表有多种实现方式,常见的有单向链表、双向链表和循环链表。下面以单向链表为例,介绍链表的实现方法。
定义链表节点结构体:
typedef struct ListNode {
int val; // 数据元素struct ListNode* next; // 指向下一个节点的指针
} ListNode;
定义链表结构体:
typedef struct LinkedList {
ListNode* head; // 头节点指针int size; // 链表长度
} LinkedList;
链表的基本操作包括插入、删除和查找。
插入操作:
void insert(LinkedList* list, int val, int index) {
// 新建节点
ListNode* new_node = (ListNode*) malloc(sizeof(ListNode));
new_node->val = val;
new_node->next = NULL;
// 插入节点
if (index <= 0) {
new_node->next = list->head;
list->head = new_node;
} else {
ListNode* cur = list->head;
for (int i = 0; i < index - 1 && cur != NULL; i++) {
cur = cur->next;
}
if (cur != NULL) {
new_node->next = cur->next;
cur->next = new_node;
}
}
list->size++;
}
删除操作:
void delete(LinkedList* list, int index) {
if (list->head == NULL) {
return;
}
// 删除头节点if (index == 0) {
ListNode* cur = list->head;
list->head = cur->next;
free(cur);
} else {
ListNode* cur = list->head;
for (int i = 0; i < index - 1 && cur != NULL; i++) {
cur = cur->next;
}
if (cur != NULL && cur->next != NULL) {
ListNode* tmp = cur->next;
cur->next = tmp->next;
free(tmp);
}
}
list->size--;
}
查找操作:
int find(LinkedList* list, int val) {
ListNode* cur = list->head;
int index = 0;
while (cur != NULL && cur->val != val) {
cur = cur->next;
index++;
}
if (cur != NULL) {
return index;
} else {
return -1;
}
}
这是一个简单的单向链表的实现,还有很多细节需要考虑,例如链表的初始化、销毁等操作,具体实现可以根据需求进行扩展和修改。
3、栈(Stack)
3.1、基本原理
栈是一种线性数据结构,具有后进先出的特点,可以理解为一种特殊的数组,只能在栈顶进行插入和删除操作。
3.2、实现
数组实现栈
数组实现栈需要定义一个数组和一个变量来表示栈顶的位置。栈顶的位置初始值为-1,表示栈为空。每次入栈时,将栈顶位置加1,并将要入栈的元素存储在栈顶位置上;每次出栈时,将栈顶位置减1,并返回栈顶元素。如果栈顶位置小于0,则表示栈为空。
以下是一个简单的数组实现栈的示例代码:
#define MAX_SIZE 10 // 定义栈的最大容量
int stack[MAX_SIZE]; // 定义栈的数组
int top = -1; // 定义栈顶位置
// 判断栈是否为空
int is_empty() {
return top < 0;
}
// 判断栈是否已满
int is_full() {
return top >= MAX_SIZE - 1;
}
// 入栈
void push(int value) {
if (is_full()) {
printf("Stack overflow!\n");
return;
}
stack[++top] = value;
}
// 出栈
int pop() {
if (is_empty()) {
printf("Stack underflow!\n");
return -1;
}
return stack[top--];
}
// 获取栈顶元素
int get_top() {
if (is_empty()) {
printf("Stack is empty!\n");
return -1;
}
return stack[top];
}
链表实现栈
链表实现栈需要定义一个链表的头结点,每次入栈时将新的元素插入到链表的头部,每次出栈时删除链表头部的元素。头结点的next指针指向链表的第一个元素,表示栈顶的位置。如果头结点的next指针为空,则表示栈为空。
以下是一个简单的链表实现栈的示例代码:
typedef struct node {
int data;
struct node* next;
} Node;
Node* top = NULL; // 定义栈顶指针
// 判断栈是否为空
int is_empty() {
return top == NULL;
}
// 入栈
void push(int value) {
Node* new_node = (Node*)malloc(sizeof(Node));
new_node->data = value;
new_node->next = top;
top = new_node;
}
// 出栈
int pop() {
if (is_empty()) {
printf("Stack underflow!\n");
return -1;
}
Node* temp = top;
int value = temp->data;
top = top->next;
free(temp);
return value;
}
// 获取栈顶元素
int get_top() {
if (is_empty()) {
printf("Stack is empty!\n");
return -1;
}
return top->data;
}
4、队列
4.1、基本原理
队列是一种先进先出(First-In-First-Out,FIFO)的线性数据结构,它可以在队尾插入元素,在队头删除元素。队列具有“先进先出”的特点,就像我们平时排队一样,先来的先离开,后来的后离开。
4.2、实现
数组实现
C语言中可以通过结构体来实现队列。一个队列需要两个指针,一个指向队头,一个指向队尾,以及一个数组来存储队列中的元素。队头指针指向队列中的第一个元素,队尾指针指向队列中的最后一个元素。在插入元素时,先将元素插入到队尾指针指向的位置,然后将队尾指针后移一位。在删除元素时,先将队头指针后移一位,然后返回队头指针指向的元素。
以下是C语言实现队列的示例代码:
#define MAX_QUEUE_SIZE 100
typedef struct {
int data[MAX_QUEUE_SIZE];
int front; // 队首指针
int rear; // 队尾指针
} Queue;
// 初始化队列
void initQueue(Queue *q) {
q->front = 0;
q->rear = 0;
}
// 判断队列是否为空
int isQueueEmpty(Queue *q) {
return q->front == q->rear;
}
// 判断队列是否已满
int isQueueFull(Queue *q) {
return q->rear == MAX_QUEUE_SIZE;
}
// 入队
void enqueue(Queue *q, int x) {
if (isQueueFull(q)) {
printf("Queue is full.\n");
return;
}
q->data[q->rear++] = x;
}
// 出队
int dequeue(Queue *q) {
if (isQueueEmpty(q)) {
printf("Queue is empty.\n");
return -1;
}
int x = q->data[q->front++];
return x;
}
以上代码中,Queue结构体包含一个整型数组data,以及两个指针front和rear,分别指向队首和队尾。initQueue函数用于初始化队列,将队首和队尾指针都指向数组的第一个元素。isQueueEmpty和isQueueFull函数分别用于判断队列是否为空和已满。enqueue函数用于入队,将元素插入到队列的队尾,同时更新队尾指针。dequeue函数用于出队,从队列的队首取出元素,同时更新队首指针。需要注意的是,出队时需要判断队列是否为空,如果为空则返回-1。
链表实现
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node* next;
} Node;
typedef struct Queue {
Node* front;
Node* rear;
} Queue;
// 初始化队列
void initQueue(Queue* q) {
q->front = q->rear = NULL;
}
// 判断队列是否为空
int isEmpty(Queue* q) {
return q->front == NULL;
}
// 入队
void enqueue(Queue* q, int data) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
newNode->next = NULL;
if (isEmpty(q)) {
q->front = q->rear = newNode;
} else {
q->rear->next = newNode;
q->rear = newNode;
}
}
// 出队
int dequeue(Queue* q) {
if (isEmpty(q)) {
printf("Error: Queue is empty\n");
return -1;
}
Node* temp = q->front;
int data = temp->data;
if (q->front == q->rear) {
q->front = q->rear = NULL;
} else {
q->front = q->front->next;
}
free(temp);
return data;
}
// 打印队列
void printQueue(Queue* q) {
if (isEmpty(q)) {
printf("Empty queue\n");
return;
}
Node* temp = q->front;
while (temp != NULL) {
printf("%d ", temp->data);
temp = temp->next;
}
printf("\n");
}
int main() {
Queue q;
initQueue(&q);
enqueue(&q, 1);
enqueue(&q, 2);
enqueue(&q, 3);
printQueue(&q);
dequeue(&q);
printQueue(&q);
dequeue(&q);
printQueue(&q);
dequeue(&q);
printQueue(&q);
dequeue(&q);
return 0;
}
这个队列的实现基于链表,有 Node 和 Queue 两个结构体。其中 Node 表示链表中的一个节点,包含数据和指向下一个节点的指针;Queue 表示队列,包含指向队首和队尾节点的指针。
队列的初始化操作 initQueue 会将队列的头指针和尾指针都设置为 NULL。
入队操作 enqueue 会新建一个节点,将数据存入节点中,并将节点加入到队列的尾部。如果队列为空,头指针和尾指针都指向新节点。
出队操作 dequeue 会将队列的头节点取出来,返回节点中的数据,并将头指针指向下一个节点。如果队列为空,则会输出错误信息并返回 -1。
打印队列的操作 printQueue 会依次遍历链表中的节点,输出节点中的数据。如果队列为空,则输出 "Empty queue"。
5、二叉树
5.1、基本原理
二叉树是一种常见的数据结构,它由一个根节点以及左右两个子树组成,每个子树也是一个二叉树。二叉树中的每个节点最多有两个子节点,左子节点和右子节点。它们的顺序是有意义的,即左子节点小于父节点,右子节点大于父节点。
5.2、实现
在C语言中,可以使用结构体和指针来实现二叉树。二叉树结构体通常包含三个成员变量:值、左子节点和右子节点。
以下是一个二叉树的结构体定义及其基本操作的实现。
// 二叉树结构体定义
typedef struct TreeNode {
int val;
struct TreeNode* left;
struct TreeNode* right;
} TreeNode;
// 创建一个新节点
TreeNode* createNode(int val) {
TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));
node->val = val;
node->left = NULL;
node->right = NULL;
return node;
}
// 在二叉搜索树中插入节点
TreeNode* insertNode(TreeNode* root, int val) {
if (root == NULL) {
return createNode(val);
}
if (val < root->val) {
root->left = insertNode(root->left, val);
} else {
root->right = insertNode(root->right, val);
}
return root;
}
// 在二叉树中查找节点
TreeNode* findNode(TreeNode* root, int val) {
if (root == NULL) {
return NULL;
}
if (val == root->val) {
return root;
}
if (val < root->val) {
return findNode(root->left, val);
} else {
return findNode(root->right, val);
}
}
// 删除二叉搜索树中的节点
TreeNode* deleteNode(TreeNode* root, int val) {
if (root == NULL) {
return NULL;
}
if (val < root->val) {
root->left = deleteNode(root->left, val);
} else if (val > root->val) {
root->right = deleteNode(root->right, val);
} else {
// 当前节点就是要删除的节点
if (root->left == NULL) {
TreeNode* temp = root->right;
free(root);
return temp;
} else if (root->right == NULL) {
TreeNode* temp = root->left;
free(root);
return temp;
} else {
// 当前节点有两个子节点
TreeNode* temp = root->right;
while (temp->left != NULL) {
temp = temp->left;
}
root->val = temp->val;
root->right = deleteNode(root->right, temp->val);
}
}
return root;
}
二叉树的遍历可以分为三种:前序遍历、中序遍历和后序遍历。其中,前序遍历的遍历顺序是先遍历根节点,然后遍历左子树,最后遍历右子树;中序遍历的遍历顺序是先遍历左子树,然后遍历根节点,最后遍历右子树;后序遍历的遍历顺序是先遍历左子树,然后遍历右子树,最后遍历根节点。
递归实现:
#include <stdio.h>
#include <stdlib.h>
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
};
// 前序遍历
void preorderTraversal(struct TreeNode* root){
if(root == NULL) return;
printf("%d ", root->val);
preorderTraversal(root->left);
preorderTraversal(root->right);
}
// 中序遍历
void inorderTraversal(struct TreeNode* root){
if(root == NULL) return;
inorderTraversal(root->left);
printf("%d ", root->val);
inorderTraversal(root->right);
}
// 后序遍历
void postorderTraversal(struct TreeNode* root){
if(root == NULL) return;
postorderTraversal(root->left);
postorderTraversal(root->right);
printf("%d ", root->val);
}
int main() {
// 构建一个二叉树
struct TreeNode* root = (struct TreeNode*)malloc(sizeof(struct TreeNode));
root->val = 1;
root->left = (struct TreeNode*)malloc(sizeof(struct TreeNode));
root->left->val = 2;
root->right = (struct TreeNode*)malloc(sizeof(struct TreeNode));
root->right->val = 3;
root->left->left = NULL;
root->left->right = NULL;
root->right->left = (struct TreeNode*)malloc(sizeof(struct TreeNode));
root->right->left->val = 4;
root->right->right = (struct TreeNode*)malloc(sizeof(struct TreeNode));
root->right->right->val = 5;
root->right->left->left = NULL;
root->right->left->right = NULL;
root->right->right->left = NULL;
root->right->right->right = NULL;
printf("前序遍历:");
preorderTraversal(root);
printf("\n中序遍历:");
inorderTraversal(root);
printf("\n后序遍历:");
postorderTraversal(root);
printf("\n");
return 0;
}
以上代码中,我们定义了一个TreeNode结构体,表示二叉树的节点。其中,val表示节点的值,left和right分别表示该节点的左右子节点。我们通过递归的方式实现了三种遍历方式,分别为前序遍历、中序遍历和后序遍历。最后,我们在main函数中构建了一个二叉树,并对其进行了三种遍历,输出了结果。
非递归实现:
先序遍历:
void pre_order_non_recursive(Node* root) {
if (root == NULL) {
return;
}
stack<Node*> s;
s.push(root);
while (!s.empty()) {
Node* cur = s.top();
s.pop();
printf("%d ", cur->data);
if (cur->right != NULL) {
s.push(cur->right);
}
if (cur->left != NULL) {
s.push(cur->left);
}
}
}
中序遍历:
void in_order_non_recursive(Node* root) {
stack<Node*> s;
Node* cur = root;
while (cur != NULL || !s.empty()) {
while (cur != NULL) {
s.push(cur);
cur = cur->left;
}
cur = s.top();
s.pop();
printf("%d ", cur->data);
cur = cur->right;
}
}
后序遍历:
void post_order_non_recursive(Node* root) {
if (root == NULL) {
return;
}
stack<Node*> s;
s.push(root);
Node* last_visited = NULL;
while (!s.empty()) {
Node* cur = s.top();
if ((cur->left == NULL && cur->right == NULL) || (last_visited != NULL && (last_visited == cur->left || last_visited == cur->right))) {
printf("%d ", cur->data);
s.pop();
last_visited = cur;
} else {
if (cur->right != NULL) {
s.push(cur->right);
}
if (cur->left != NULL) {
s.push(cur->left);
}
}
}
}
这些非递归实现的代码利用了栈的特性,将待访问节点放入栈中,按照一定的顺序遍历整个树。在遍历的过程中,通过判断栈顶元素的左右子节点是否被访问过,来决定是否访问该节点,以此达到遍历整个树的目的。
6、哈希表
6.1、基本原理
哈希表是一种基于哈希函数实现的数据结构,它可以支持高效的插入、查找和删除操作。它的基本原理是将数据映射到哈希表的槽位(slot)中,这个映射过程是通过哈希函数实现的。哈希函数将数据映射到槽位的过程应该是快速且随机的,这样可以尽量避免哈希冲突(即不同的数据映射到了同一个槽位中)。
6.2、实现
哈希表的实现一般有两种方式:拉链法和开放地址法。
- 拉链法:将哈希表的每个槽位都作为链表的头节点,哈希函数将数据映射到的槽位上的数据会被插入到对应链表中。这种方法处理哈希冲突的方式是将冲突的数据插入到链表中。插入和查找的时间复杂度都是 O(1),但由于链表的缘故,需要额外的空间存储链表节点。
- 开放地址法:当哈希冲突发生时,哈希表的某个槽位已经被占用,此时可以向后探查其他槽位,直到找到空闲的槽位为止。这种方法处理哈希冲突的方式是在哈希表中寻找另一个可用的槽位。插入和查找的时间复杂度都是 O(1),但是在哈希表的负载因子比较大的情况下,探查槽位的时间可能会比较长。
下面是一个简单的拉链法哈希表的实现示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_TABLE_SIZE 1000
/* 哈希表的节点结构 */
struct Node {
char key[20]; // 存储键值
int value; // 存储值
struct Node *next; // 指向下一个节点的指针
};
/* 哈希表结构 */
struct HashTable {
struct Node *table[MAX_TABLE_SIZE]; // 哈希表
int size; // 哈希表中节点数目
};
/* 初始化哈希表 */
void initHashTable(struct HashTable *hashTable) {
int i;
for (i = 0; i < MAX_TABLE_SIZE; i++) {
hashTable->table[i] = NULL;
}
hashTable->size = 0;
}
/* 计算哈希值 */
int hash(char *key) {
int hash = 0;
while (*key) {
hash += *key++;
}
return hash % MAX_TABLE_SIZE;
}
/* 添加节点 */
void put(struct HashTable *hashTable, char *key, int value) {
int index = hash(key);
struct Node *node = (struct Node *) malloc(sizeof(struct Node));
strcpy(node->key, key);
node->value = value;
node->next = hashTable->table[index];
hashTable->table[index] = node;
hashTable->size++;
}
/* 查找节点 */
struct Node *get(struct HashTable *hashTable, char *key) {
int index = hash(key);
struct Node *node = hashTable->table[index];
while (node != NULL && strcmp(node->key, key) != 0) {
node = node->next;
}
return node;
}
/* 删除节点 */
void removeNode(struct HashTable *hashTable, char *key) {
int index = hash(key);
struct Node *prev = NULL;
struct Node *node = hashTable->table[index];
while (node != NULL && strcmp(node->key, key) != 0) {
prev = node;
node = node->next;
}
if (node == NULL) {
return;
}
if (prev == NULL) {
hashTable->table[index] = node->next;
} else {
prev->next = node->next;
}
free(node);
hashTable->size--;
}
/* 打印哈希表 */
void printHashTable(struct HashTable *hashTable) {
int i;
for (i = 0; i < MAX_TABLE_SIZE; i++) {
printf("%d: ", i);
struct Node *node = hashTable->table[i];
while (node != NULL) {
printf("(%s, %d) ", node->key, node->value);
node = node->next;
}
printf("\n");
}
}
7、汉诺塔:
7.1、递归实现
#include <stdio.h>
void hanoi(int n, char A, char B, char C)
{
if (n == 1) {
printf("Move disk %d from %c to %c\n", n, A, C);
} else {
hanoi(n - 1, A, C, B);
printf("Move disk %d from %c to %c\n", n, A, C);
hanoi(n - 1, B, A, C);
}
}
int main()
{
int n = 3;
hanoi(n, 'A', 'B', 'C');
return 0;
}
其中,hanoi函数表示汉诺塔的移动过程,n表示当前移动的盘子数量,A、B、C表示三根柱子的名称。当只有一个盘子时,直接将其从A柱子移动到C柱子即可;否则,将前n-1个盘子从A柱子移动到B柱子,然后将第n个盘子从A柱子移动到C柱子,最后将前n-1个盘子从B柱子移动到C柱子。
7.2、非递归实现
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 100
typedef struct {
int disk; // 当前盘子大小
int from; // 当前所在柱子
int to; // 目标柱子
int aux; // 辅助柱子
} Move;
typedef struct {
Move data[MAX_SIZE];
int top;
} Stack;
void initStack(Stack *s) {
s->top = -1;
}
int isEmpty(Stack *s) {
return s->top == -1;
}
int isFull(Stack *s) {
return s->top == MAX_SIZE - 1;
}
int push(Stack *s, Move item) {
if (isFull(s)) {
return 0;
}
s->data[++s->top] = item;
return 1;
}
Move pop(Stack *s) {
if (isEmpty(s)) {
Move error = { -1, -1, -1, -1 };
return error;
}
return s->data[s->top--];
}
void move(int disk, int from, int to, int aux) {
Stack s;
initStack(&s);
Move firstMove = { disk, from, to, aux };
push(&s, firstMove);
while (!isEmpty(&s)) {
Move currentMove = pop(&s);
int currentDisk = currentMove.disk;
int currentFrom = currentMove.from;
int currentTo = currentMove.to;
int currentAux = currentMove.aux;
if (currentDisk == 1) {
printf("Move disk %d from %d to %d\n", currentDisk, currentFrom, currentTo);
} else {
Move newMove1 = { currentDisk - 1, currentFrom, currentAux, currentTo };
Move newMove2 = { 1, currentFrom, currentTo, currentAux };
Move newMove3 = { currentDisk - 1, currentAux, currentTo, currentFrom };
push(&s, newMove3);
push(&s, newMove2);
push(&s, newMove1);
}
}
}
int main() {
int n = 3;
int from = 1, to = 3, aux = 2;
move(n, from, to, aux);
return 0;
}
该程序的实现方式和递归实现类似,但是使用了栈来保存每一次移动的状态,并在栈不为空时不断地弹出栈顶元素进行移动。在移动过程中,如果当前盘子数为1,则直接输出移动步骤;否则,根据汉诺塔的规则,将大盘子移动到辅助柱子上,然后将最后一个盘子移动到目标柱子上,最后将所有的盘子从辅助柱子上移动到目标柱子上。
8、斐波那契数列
可以使用递归函数来实现斐波那契数列。递归函数是指在函数体内调用函数本身的函数。在实现斐波那契数列时,可以定义一个递归函数,该函数将调用自身两次以生成下一个数字,并在基本情况下返回1或0。
8.1、递归实现
#include <stdio.h>
int fibonacci(int n) {
if (n <= 1) {
return n;
}
return fibonacci(n-1) + fibonacci(n-2);
}
int main() {
int n, i;
printf("Enter the number of terms: ");
scanf("%d", &n);
printf("Fibonacci Series: ");
for (i = 0; i < n; i++) {
printf("%d ", fibonacci(i));
}
return 0;
}
在此代码中,函数fibonacci()是递归函数,它将调用自身两次以生成下一个数字,并在n <= 1的情况下返回n。在主函数中,我们获取了要生成的斐波那契数列的项数,并使用for循环调用递归函数fibonacci()来生成数列。
8.2、非递归实现
#include <stdio.h>
int fibonacci(int n) {
int f0 = 0, f1 = 1, fn = n;
if (n <= 1) {
return n;
} else {
for (int i = 2; i <= n; i++) {
fn = f0 + f1;
f0 = f1;
f1 = fn;
}
return fn;
}
}
int main() {
int n = 10;
printf("The %d-th fibonacci number is: %d\n", n, fibonacci(n));
return 0;
}
代码的思路是利用一个循环,从第二项开始计算斐波那契数列的每一项,通过更新前两项的值来得到当前项的值,直到计算到第 n 项,最后返回第 n 项的值。该算法的时间复杂度为 O(n),空间复杂度为 O(1)。
9、杨辉三角
9.1、递归实现
#include <stdio.h>
int pascal(int row, int col) {
if (row == col || col == 0) { // 杨辉三角的左右两边都是1
return 1;
} else { // 杨辉三角的每个数等于它上方两个数之和
return pascal(row - 1, col - 1) + pascal(row - 1, col);
}
}
int main() {
int n;
printf("请输入杨辉三角的行数:");
scanf("%d", &n);
for (int i = 0; i < n; i++) {
for (int j = 0; j <= i; j++) {
printf("%d ", pascal(i, j));
}
printf("\n");
}
return 0;
}
该程序首先输入一个正整数n表示要输出的杨辉三角的行数,然后通过两重循环遍历每一行的每一个位置,调用递归函数pascal计算出该位置上的数值,并将其输出到控制台上。递归函数pascal的实现采用了杨辉三角的定义,当当前位置处于杨辉三角的左右两边时,返回1,否则返回该位置上方两个数之和。
9.2、非递归实现
#include <stdio.h>
void printPascalTriangle(int rows) {
int triangle[rows][rows];
for (int i = 0; i < rows; i++) {
for (int j = 0; j <= i; j++) {
if (j == 0 || j == i) {
triangle[i][j] = 1;
} else {
triangle[i][j] = triangle[i - 1][j - 1] + triangle[i - 1][j];
}
printf("%d ", triangle[i][j]);
}
printf("\n");
}
}
int main() {
int rows;
printf("Enter the number of rows: ");
scanf("%d", &rows);
printf("Pascal's Triangle:\n");
printPascalTriangle(rows);
return 0;
}
该算法通过二维数组存储杨辉三角的每一个元素,从而避免了递归算法中的重复计算。