UNSWCOMP2521数据结构算法澳洲CS

UNSW COMP2521 完整攻略:数据结构与算法 C 指针 / 树 / 图 / 复杂度分析

6 min read

UNSW COMP2521 Data Structures and Algorithms 是 CS 专业大二的核心课程,也是大多数学生第一次系统接触 C 语言指针操作算法复杂度分析的课程。

很多同学在 COMP1511 里学过 C,却在 COMP2521 的链表和树操作上翻车——因为这门课考察的不只是"写出来",而是"写对 + 写高效 + 能证明为什么高效"。


COMP2521 课程结构

组成部分权重说明
Assignment 1(链表/树实现)20–25%C 实现,Autotest 评分
Assignment 2(图算法)20–25%C 实现,BFS/DFS/Dijkstra
Lab Exercises10%每周 Lab,当场或次日提交
Final Exam40–45%笔试,代码题+分析题

第一部分:C 指针与内存管理——最大难点

1.1 指针基础回顾

int x = 10;
int *p = &x;    // p 存储 x 的地址

printf("%d\n", x);   // 10(变量值)
printf("%p\n", p);   // x 的内存地址(如 0x7fff5...)
printf("%d\n", *p);  // 10(解引用,访问地址处的值)

*p = 20;             // 通过指针修改 x 的值
printf("%d\n", x);   // 20

1.2 动态内存分配(malloc / free)

// ✅ 正确的动态分配模式
int *arr = malloc(n * sizeof(int));
if (arr == NULL) {
    fprintf(stderr, "malloc failed\n");
    exit(1);
}

// 使用数组...
arr[0] = 1;
arr[1] = 2;

// 用完必须 free!否则内存泄漏
free(arr);
arr = NULL;  // 好习惯:free 后置 NULL,防止野指针

1.3 链表节点的定义与基本操作

typedef struct node {
    int value;
    struct node *next;
} Node;

// 创建新节点
Node *newNode(int val) {
    Node *n = malloc(sizeof(Node));
    assert(n != NULL);
    n->value = val;
    n->next = NULL;
    return n;
}

// 在链表头部插入
Node *insertFront(Node *head, int val) {
    Node *n = newNode(val);
    n->next = head;
    return n;  // 返回新的头节点
}

最常见的 Segmentation Fault 原因

  • 对 NULL 指针解引用((*null_ptr).field
  • free 之后继续访问(野指针)
  • 递归没有 base case,栈溢出

1.4 调试工具

# 使用 valgrind 检测内存问题(Linux/CSE 服务器)
valgrind --leak-check=full ./your_program

# 用 AddressSanitizer 编译(更快)
gcc -fsanitize=address -g -o program program.c
./program

第二部分:树(Trees)

2.1 二叉搜索树(BST)基本操作

BST 性质:对任意节点,左子树所有值 < 节点值 < 右子树所有值。

typedef struct bstNode {
    int value;
    struct bstNode *left;
    struct bstNode *right;
} BST;

// BST 插入(递归)
BST *bstInsert(BST *t, int val) {
    if (t == NULL) {
        // 创建新节点
        BST *new = malloc(sizeof(BST));
        new->value = val;
        new->left = new->right = NULL;
        return new;
    }
    if (val < t->value)
        t->left = bstInsert(t->left, val);
    else if (val > t->value)
        t->right = bstInsert(t->right, val);
    // val == t->value:不插入重复值
    return t;
}

// 中序遍历(Inorder)→ 输出排好序的结果
void inorder(BST *t) {
    if (t == NULL) return;
    inorder(t->left);
    printf("%d ", t->value);
    inorder(t->right);
}

2.2 AVL 树——自平衡 BST

AVL 树通过旋转保持平衡(任意节点左右子树高度差 ≤ 1)。

旋转类型

  • Left Rotation(右子树过重)
  • Right Rotation(左子树过重)
  • Left-Right Rotation(左-右双旋)
  • Right-Left Rotation(右-左双旋)

高度计算

int height(BST *t) {
    if (t == NULL) return -1;
    int lh = height(t->left);
    int rh = height(t->right);
    return 1 + (lh > rh ? lh : rh);
}

int balanceFactor(BST *t) {
    return height(t->left) - height(t->right);
}

考试关键:会判断什么时候需要旋转,以及旋转后树的结构——手画一遍比记公式有效。


第三部分:图算法

3.1 图的两种表示

// 邻接矩阵(稠密图,V² 空间)
int adj[MAX_V][MAX_V] = {0};
adj[u][v] = 1;  // 无向图:adj[v][u] = 1 也要设

// 邻接链表(稀疏图,更省空间)
typedef struct adjNode {
    int dest;
    struct adjNode *next;
} AdjNode;

AdjNode *graph[MAX_V];  // graph[v] 是 v 的邻居链表

3.2 BFS(广度优先搜索)

BFS 用于:最短路径(无权图)、层级遍历

void bfs(int graph[][MAX_V], int n, int start) {
    int visited[MAX_V] = {0};
    int queue[MAX_V];
    int front = 0, rear = 0;

    queue[rear++] = start;
    visited[start] = 1;

    while (front < rear) {
        int v = queue[front++];
        printf("%d ", v);

        for (int u = 0; u < n; u++) {
            if (graph[v][u] && !visited[u]) {
                queue[rear++] = u;
                visited[u] = 1;
            }
        }
    }
}

3.3 DFS(深度优先搜索)

DFS 用于:连通性检测、拓扑排序、环检测

void dfs(int graph[][MAX_V], int visited[], int v) {
    visited[v] = 1;
    printf("%d ", v);
    
    for (int u = 0; u < MAX_V; u++) {
        if (graph[v][u] && !visited[u]) {
            dfs(graph, visited, u);
        }
    }
}

3.4 Dijkstra 最短路径

// 简化版 Dijkstra(邻接矩阵,适合考试)
void dijkstra(int graph[][MAX_V], int n, int src) {
    int dist[MAX_V], visited[MAX_V];
    for (int i = 0; i < n; i++) {
        dist[i] = INT_MAX;
        visited[i] = 0;
    }
    dist[src] = 0;

    for (int count = 0; count < n - 1; count++) {
        // 找未访问中距离最小的节点 u
        int u = -1;
        for (int v = 0; v < n; v++)
            if (!visited[v] && (u == -1 || dist[v] < dist[u]))
                u = v;
        
        visited[u] = 1;

        // 更新邻居距离
        for (int v = 0; v < n; v++) {
            if (graph[u][v] && !visited[v] &&
                dist[u] + graph[u][v] < dist[v]) {
                dist[v] = dist[u] + graph[u][v];
            }
        }
    }
}

第四部分:复杂度分析

4.1 常见操作时间复杂度

数据结构查找插入删除
数组(有序)O(log n)O(n)O(n)
链表O(n)O(1)(头部)O(n)
BST(平均)O(log n)O(log n)O(log n)
BST(最差/退化)O(n)O(n)O(n)
AVL 树O(log n)O(log n)O(log n)
哈希表(平均)O(1)O(1)O(1)

4.2 排序算法复杂度

算法BestAverageWorst稳定性
Bubble SortO(n)O(n²)O(n²)
Insertion SortO(n)O(n²)O(n²)
Merge SortO(n log n)O(n log n)O(n log n)
Quick SortO(n log n)O(n log n)O(n²)
Heap SortO(n log n)O(n log n)O(n log n)

4.3 主定理(Master Theorem)

对于递推 T(n) = aT(n/b) + f(n):

  • 若 f(n) = O(n^log_b(a) - ε):T(n) = Θ(n^log_b(a))
  • 若 f(n) = Θ(n^log_b(a)):T(n) = Θ(n^log_b(a) · log n)
  • 若 f(n) = Ω(n^log_b(a) + ε):T(n) = Θ(f(n))

实际例子

  • 归并排序:T(n) = 2T(n/2) + O(n) → a=2, b=2, log₂2=1 → Case 2 → O(n log n)
  • 二分查找:T(n) = T(n/2) + O(1) → a=1, b=2, log₂1=0 → Case 2 → O(log n)

COMP2521 期末考试题型

题型分值比例备考重点
手写 C 代码(链表/树操作)30–40%熟练写 BST 插入/删除
图算法(BFS/DFS/Dijkstra 追踪)20–25%手画执行过程
复杂度分析15–20%Big-O 推导+主定理
概念问答(为什么用AVL而非BST)15%优缺点对比

考试技巧:图算法题通常要求你手工追踪执行过程(画出每步的状态),而不是写代码——这比写代码更考察理解。


需要辅导?

COMP2521 的 Assignment 调试(Segmentation Fault 定位)和期末考前的算法追踪练习是最多同学需要帮助的环节。我们的 UNSW CS 导师大多数都考过 COMP2521,熟悉 Autotest 的评分规则。

相关资源:

💻

代码跑不通?作业逻辑卡住了?

Deadline 前搞定。发送代码/题目给客服,30 分钟内评估,安排 CS 专业导师。

扫码咨询发 Brief · 30 分钟报价