Tìm số lớn thứ 2 trong mảng 1 chiều

Tìm số lớn thứ 2 trong mảng là một bài toán phổ biến trong lập trình, thường xuất hiện trong các cuộc phỏng vấn và các kỳ thi lập trình. Bài viết này sẽ hướng dẫn bạn cách giải quyết bài toán này một cách hiệu quả nhất.

Giới thiệu bài toán

Bài toán tìm số lớn thứ 2 trong mảng yêu cầu chúng ta:

  • Cho một mảng số nguyên
  • Tìm phần tử có giá trị lớn thứ 2 trong mảng
  • Xử lý các trường hợp đặc biệt như mảng có phần tử trùng nhau
Tìm số lớn thứ 2 trong mảng 1 chiều

Phân tích bài toán

Khi giải bài toán này, bạn nên chú ý đến các trường hợp đặc biệt sau đây:

  1. Mảng có ít hơn 2 phần tử
  2. Mảng có các phần tử giống nhau
  3. Mảng không có số lớn thứ 2 (tất cả phần tử bằng nhau)

Trước khi đi vào các phương pháp giải quyết, bạn hãy dành một chút thời gian để tự mình suy nghĩ về bài toán này. Việc tự tìm ra lời giải sẽ giúp bạn phát triển tư duy giải quyết vấn đề và ghi nhớ kiến thức tốt hơn. Sau khi đã có ý tưởng của riêng mình, bạn có thể tham khảo các phương pháp được trình bày dưới đây để hoàn thiện giải pháp của mình.

Thực hành nhiều bài tập hơn tại https://luyencode.net


Các phương pháp giải

Chúng ta sẽ đi từ phương án dễ triển khai (ít tối ưu) đến phương án đòi hỏi suy luận nhiều hơn (tối ưu) nhé.

Phương pháp sắp xếp

Ý tưởng:

  • Sắp xếp mảng theo thứ tự giảm dần
  • Duyệt mảng để tìm phần tử khác với số lớn nhất đầu tiên
  • Độ phức tạp: O(nlogn)

Giải thích chi tiết:

  1. Kiểm tra điều kiện đầu vào:
    • Nếu mảng có ít hơn 2 phần tử, trả về -1
  2. Sắp xếp mảng:
    • Sử dụng hàm sort với greater() để sắp xếp theo thứ tự giảm dần
    • Sau khi sắp xếp, phần tử lớn nhất sẽ ở vị trí đầu tiên (arr[0])
  3. Tìm số lớn thứ 2:
    • Duyệt mảng từ vị trí thứ 2
    • Trả về phần tử đầu tiên khác với số lớn nhất
    • Nếu không tìm thấy, trả về -1

Code mẫu

#include <iostream>
#include <algorithm>
using namespace std;

int findSecondMaxSort(int arr[], int n) {
    // Check input condition
    if (n < 2) {
        return -1; // Array doesn't have enough elements
    }
    
    // Sort array in descending order
    sort(arr, arr + n, greater<int>());
    
    // Find first element different from max
    for (int i = 1; i < n; i++) {
        if (arr[i] != arr[0]) {
            return arr[i];
        }
    }
    
    return -1; // No second largest element found
}

int main() {
    int arr[] = {12, 35, 1, 10, 34, 1};
    int n = sizeof(arr)/sizeof(arr[0]);
    
    int second = findSecondMaxSort(arr, n);
    if (second != -1) {
        cout << "Second largest element is: " << second << endl;
    } else {
        cout << "No second largest element found" << endl;
    }
    
    return 0;
}
import java.util.Arrays;
import java.util.Collections;

public class SecondMaxFinder {
    public static int findSecondMaxSort(Integer[] arr, int n) {
        // Check input condition
        if (n < 2) {
            return -1; // Array doesn't have enough elements
        }
        
        // Sort array in descending order
        Arrays.sort(arr, Collections.reverseOrder());
        
        // Find first element different from max
        for (int i = 1; i < n; i++) {
            if (arr[i] != arr[0]) {
                return arr[i];
            }
        }
        
        return -1; // No second largest element found
    }
    
    public static void main(String[] args) {
        Integer[] arr = {12, 35, 1, 10, 34, 1};
        int n = arr.length;
        
        int second = findSecondMaxSort(arr, n);
        if (second != -1) {
            System.out.println("Second largest element is: " + second);
        } else {
            System.out.println("No second largest element found");
        }
    }
}
def find_second_max_sort(arr, n):
    # Check input condition
    if n < 2:
        return -1  # Array doesn't have enough elements
    
    # Sort array in descending order
    arr.sort(reverse=True)
    
    # Find first element different from max
    for i in range(1, n):
        if arr[i] != arr[0]:
            return arr[i]
    
    return -1  # No second largest element found

if __name__ == "__main__":
    arr = [12, 35, 1, 10, 34, 1]
    n = len(arr)
    
    second = find_second_max_sort(arr, n)
    if second != -1:
        print("Second largest element is:", second)
    else:
        print("No second largest element found")

Phương pháp duyệt 2 lần

Ý tưởng

  • Lần 1: Tìm số lớn nhất
  • Lần 2: Tìm số lớn nhất trong các số nhỏ hơn max
  • Độ phức tạp: O(n)

Giải thích chi tiết

  1. Kiểm tra điều kiện đầu vào:
    • Nếu mảng có ít hơn 2 phần tử, trả về -1
  2. Lần duyệt thứ nhất:
    • Tìm số lớn nhất trong mảng (max)
    • Lưu giá trị này để so sánh ở lần duyệt thứ hai
  3. Lần duyệt thứ hai:
    • Tìm số lớn nhất trong các số nhỏ hơn max
    • Sử dụng second_max để lưu kết quả
    • Nếu không tìm thấy (second_max = INT_MIN), trả về -1

Code mẫu

#include <iostream>
#include <climits>
using namespace std;

int findSecondMaxTwoPass(int arr[], int n) {
    // Check input condition
    if (n < 2) {
        return -1; // Array doesn't have enough elements
    }
    
    // First pass: find maximum element
    int max = arr[0];
    for (int i = 1; i < n; i++) {
        if (arr[i] > max) {
            max = arr[i];
        }
    }
    
    // Second pass: find second maximum
    int second_max = INT_MIN;
    for (int i = 0; i < n; i++) {
        if (arr[i] != max && arr[i] > second_max) {
            second_max = arr[i];
        }
    }
    
    // Check if second maximum exists
    if (second_max == INT_MIN) {
        return -1;
    }
    
    return second_max;
}

int main() {
    int arr[] = {12, 35, 1, 10, 34, 1};
    int n = sizeof(arr)/sizeof(arr[0]);
    
    int second = findSecondMaxTwoPass(arr, n);
    if (second != -1) {
        cout << "Second largest element is: " << second << endl;
    } else {
        cout << "No second largest element found" << endl;
    }
    
    return 0;
}
#include <iostream>
#include <climits>
using namespace std;

int findSecondMaxTwoPass(int arr[], int n) {
    // Check input condition
    if (n < 2) {
        return -1; // Array doesn't have enough elements
    }
    
    // First pass: find maximum element
    int max = arr[0];
    for (int i = 1; i < n; i++) {
        if (arr[i] > max) {
            max = arr[i];
        }
    }
    
    // Second pass: find second maximum
    int second_max = INT_MIN;
    for (int i = 0; i < n; i++) {
        if (arr[i] != max && arr[i] > second_max) {
            second_max = arr[i];
        }
    }
    
    // Check if second maximum exists
    if (second_max == INT_MIN) {
        return -1;
    }
    
    return second_max;
}

int main() {
    int arr[] = {12, 35, 1, 10, 34, 1};
    int n = sizeof(arr)/sizeof(arr[0]);
    
    int second = findSecondMaxTwoPass(arr, n);
    if (second != -1) {
        cout << "Second largest element is: " << second << endl;
    } else {
        cout << "No second largest element found" << endl;
    }
    
    return 0;
}
import sys

def findSecondMaxTwoPass(arr, n):
    # Check input condition
    if n < 2:
        return -1  # Array doesn't have enough elements
    
    # First pass: find maximum element
    max_val = arr[0]
    for i in range(1, n):
        if arr[i] > max_val:
            max_val = arr[i]
    
    # Second pass: find second maximum
    second_max = -sys.maxsize - 1  # Equivalent to INT_MIN in C++
    for i in range(n):
        if arr[i] != max_val and arr[i] > second_max:
            second_max = arr[i]
    
    # Check if second maximum exists
    if second_max == -sys.maxsize - 1:
        return -1
    
    return second_max

if __name__ == "__main__":
    arr = [12, 35, 1, 10, 34, 1]
    n = len(arr)
    
    second = findSecondMaxTwoPass(arr, n)
    if second != -1:
        print("Second largest element is:", second)
    else:
        print("No second largest element found")

Phương Pháp Duyệt 1 Lần (Tối Ưu)

Ý tưởng

  • Sử dụng 2 biến để lưu số lớn nhất và lớn thứ 2
  • Cập nhật trong quá trình duyệt mảng
  • Độ phức tạp: O(n)

Giải thích chi tiết

  1. Kiểm tra điều kiện đầu vào:
    • Nếu mảng có ít hơn 2 phần tử, trả về -1
  2. Khởi tạo biến:
    • max: lưu giá trị lớn nhất
    • second_max: lưu giá trị lớn thứ 2
    • Ban đầu cả hai đều được gán giá trị INT_MIN
  3. Duyệt mảng một lần:
    • Nếu phần tử hiện tại lớn hơn max:
      • Cập nhật second_max = max cũ
      • Cập nhật max = giá trị mới
    • Nếu phần tử hiện tại lớn hơn second_max và nhỏ hơn max:
      • Cập nhật second_max = giá trị hiện tại
  4. Kiểm tra kết quả:
    • Nếu second_max vẫn là INT_MIN, không tìm thấy số lớn thứ 2
    • Ngược lại, trả về giá trị second_max

Code mẫu

#include <iostream>
#include <climits>
using namespace std;

int findSecondMax(int arr[], int n) {
    // Kiểm tra điều kiện đầu vào
    if (n < 2) {
        return -1; // Mảng không đủ phần tử
    }
    
    // Khởi tạo max và second_max
    int max = INT_MIN;
    int second_max = INT_MIN;
    
    // Duyệt mảng một lần
    for (int i = 0; i < n; i++) {
        // Nếu tìm thấy phần tử lớn hơn max
        if (arr[i] > max) {
            second_max = max; // Cập nhật second_max
            max = arr[i];     // Cập nhật max
        }
        // Nếu phần tử lớn hơn second_max và khác max
        else if (arr[i] > second_max && arr[i] < max) {
            second_max = arr[i];
        }
    }
    
    // Kiểm tra nếu không tìm thấy số lớn thứ 2
    if (second_max == INT_MIN) {
        return -1;
    }
    
    return second_max;
}

int main() {
    // Ví dụ sử dụng
    int arr[] = {12, 35, 1, 10, 34, 1};
    int n = sizeof(arr)/sizeof(arr[0]);
    
    int second = findSecondMax(arr, n);
    if (second != -1) {
        cout << "Second largest element is: " << second << endl;
    } else {
        cout << "No second largest element found" << endl;
    }
    
    return 0;
}
public class SecondMax {
    public static int findSecondMax(int[] arr, int n) {
        // Kiểm tra điều kiện đầu vào
        if (n < 2) {
            return -1; // Mảng không đủ phần tử
        }
        
        // Khởi tạo max và second_max
        int max = Integer.MIN_VALUE;
        int second_max = Integer.MIN_VALUE;
        
        // Duyệt mảng một lần
        for (int i = 0; i < n; i++) {
            // Nếu tìm thấy phần tử lớn hơn max
            if (arr[i] > max) {
                second_max = max; // Cập nhật second_max
                max = arr[i];     // Cập nhật max
            }
            // Nếu phần tử lớn hơn second_max và khác max
            else if (arr[i] > second_max && arr[i] < max) {
                second_max = arr[i];
            }
        }
        
        // Kiểm tra nếu không tìm thấy số lớn thứ 2
        if (second_max == Integer.MIN_VALUE) {
            return -1;
        }
        
        return second_max;
    }

    public static void main(String[] args) {
        // Ví dụ sử dụng
        int[] arr = {12, 35, 1, 10, 34, 1};
        int n = arr.length;
        
        int second = findSecondMax(arr, n);
        if (second != -1) {
            System.out.println("Second largest element is: " + second);
        } else {
            System.out.println("No second largest element found");
        }
    }
}
import sys

def findSecondMax(arr, n):
    # Kiểm tra điều kiện đầu vào
    if n < 2:
        return -1  # Mảng không đủ phần tử
    
    # Khởi tạo max và second_max
    max_val = -sys.maxsize - 1
    second_max = -sys.maxsize - 1
    
    # Duyệt mảng một lần
    for i in range(n):
        # Nếu tìm thấy phần tử lớn hơn max
        if arr[i] > max_val:
            second_max = max_val  # Cập nhật second_max
            max_val = arr[i]      # Cập nhật max
        # Nếu phần tử lớn hơn second_max và khác max
        elif arr[i] > second_max and arr[i] < max_val:
            second_max = arr[i]
    
    # Kiểm tra nếu không tìm thấy số lớn thứ 2
    if second_max == -sys.maxsize - 1:
        return -1
    
    return second_max

# Ví dụ sử dụng
if __name__ == "__main__":
    arr = [12, 35, 1, 10, 34, 1]
    n = len(arr)
    
    second = findSecondMax(arr, n)
    if second != -1:
        print("Second largest element is:", second)
    else:
        print("No second largest element found")

Lời kết

Tìm số lớn thứ 2 trong mảng là một bài toán tuy đơn giản nhưng đòi hỏi sự tỉ mỉ trong việc xử lý các trường hợp đặc biệt. Thuật toán one-pass được trình bày ở trên là giải pháp tối ưu nhất với độ phức tạp O(n). Hy vọng bài viết này giúp bạn hiểu rõ hơn về bài toán và các cách giải quyết hiệu quả.

Tham khảo thêm: Chinh phục 1000 bài tập lập trình (có lời giải).

Tài liệu tham khảo

  1. Introduction to Algorithms – Thomas H. Cormen.
  2. Data Structures and Algorithm Analysis – Mark Allen Weiss.

Similar Posts

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments