【第02題】給定 n,求 1 + 2 + 3 + ... + n 的和 | 四種解法


🙉自律使你自由!🙉

C語言免費動漫教程,和我一起打卡!
🌞《光天化日學C語言》🌞

LeetCode 太難?先看簡單題!
🧡《C語言入門100例》🧡

數據結構難?不存在的!
🌳《畫解數據結構》🌳

閉關刷 LeetCode,劍指大廠Offer!
🌌《算法入門指引》🌌

LeetCode 太簡單?算法學起來!
💜《夜深人靜寫算法》💜

一、題目描述

  循環輸入,每輸入一個正整數 n ( n ≤ 65535 ) n (n \le 65535) n(n65535),輸出 1 + 2 + 3 + . . . + n 1 + 2 + 3 + ... + n 1+2+3+...+n 的值,並且多輸出一個空行。當沒有任何輸入時,結束程序。

  • 這個題中,你將會學到以下的內容:

二、解題思路

難度:🔴⚪⚪⚪⚪

  • 由於 n ≤ 65535 n \le 65535 n65535,不是很大,所以我們完全可以通過循環的方式遍歷 1 到 n n n,然后將遍歷到的數字加和后進行輸出。這樣的時間復雜度是 O ( n ) O(n) O(n) 的。有關於時間復雜度相關的介紹,可以參考這篇文章:一文搞懂算法時間復雜度
  • 當然,這是一個等差數列的求和公式,所以有:
  • 1 + 2 + 3 + . . . + n = n ( n + 1 ) 2 1 + 2 + 3 + ... + n = \frac {n (n+1) } 2 1+2+3+...+n=2n(n+1)
  • 這樣,只要知道 n n n 的值,就可以在 O ( 1 ) O(1) O(1) 的時間內求得最終的答案了。接下來,讓我們來看下代碼如何實現。

三、代碼詳解

1、錯誤解法

#include <stdio.h>
int main() {
    int n;
    while (scanf("%d", &n) != EOF) {
        int ans = n * (n + 1) / 2;   // (1)
        printf("%d\n\n", ans);
    }
    return 0;
}
  • ( 1 ) (1) (1) 這行代碼直接套用等差數列求和公式。但是這里有一個小問題。
  • 因為當 n n n 取最大值 65535 65535 65535 時, n ∗ ( n + 1 ) = 65535 ∗ 65536 = ( 2 16 − 1 ) 2 16 = 2 32 − 2 16 n * (n + 1) = 65535 * 65536 = (2^{16}-1)2^{16} = 2^{32} -2^{16} n(n+1)=6553565536=(2161)216=232216,而 i n t int int 能夠表示的最大值為 2 31 − 1 2^{31}-1 2311,所以產生了溢出。就變成了負數。至於為什么溢出會變成負數,可以了解補碼相關的知識:計算機補碼詳解
  • 接下來介紹四種正確做法。

2、正確解法1:循環枚舉

#include <stdio.h>
int main() {
    int n, ans;
    while (scanf("%d", &n) != EOF) {
        ans = 0;          // (1)
        while(n) {        // (2)
            ans += n;     // (3)
            --n;          // (4)
        }
        printf("%d\n\n", ans);
    }
    return 0;
}
  • ( 1 ) (1) (1) 初始化結果ans為0;
  • ( 2 ) (2) (2) 用一個while語句來執行循環,一直自減 n,直到n減為零為止;
  • ( 3 ) (3) (3) 將當前n的值累加給 ans(循環完畢,ans就是1n的數的累加和);
  • ( 4 ) (4) (4) --n等價於n = n - 1
  • 這種方法,就是普通的枚舉,正確性容易保證,但是時間復雜度略高,為 O ( n ) O(n) O(n)

3、正確解法2:奇偶性判斷

#include <stdio.h>
int main() {
    int n, ans;
    while (scanf("%d", &n) != EOF) {
        if(n % 2 == 0) {           // (1)
            ans = n / 2 * (n+1);   // (2)
        } else {                   // (3)
            ans = (n+1) / 2 * n;   // (4)
        }
        printf("%d\n\n", ans);
    }
    return 0;
}
  • 由於 n n n n + 1 n+1 n+1 的奇偶性必然不同,所以兩者相乘必然能被 2 整除。
  • 所以我們可以得到如下情況:
  • s u m ( n ) = { ( n + 1 ) / 2 × n n 為 奇 數 n / 2 × ( n + 1 ) n 為 偶 數 sum(n) = \begin{cases} (n+1)/2 \times n & n 為奇數 \\ n/2 \times (n+1) & n 為偶數\end{cases} sum(n)={(n+1)/2×nn/2×(n+1)nn
  • 也就是根據奇偶性來決定是用 n n n 去除 2,還是 n + 1 n+1 n+1 去除 2,從而避免溢出。
  • ( 1 ) (1) (1) %在C語言中是取模的意思,a%b代表a除上b得到的余數,a%2 == 0則代表 a 為偶數,否則為奇數;這里的if判斷代表n是偶數;
  • ( 2 ) (2) (2) n為偶數時,n能被 2 整除,所以先計算 n/2,再乘上n+1
  • ( 3 ) (3) (3) 這里用到了 else語句,代表接下來要進行n為奇數的處理;
  • ( 4 ) (4) (4) n為奇數時,n+1能被 2 整除,所以先計算 (n+1)/2,再乘上n

4、正確解法3:無符號整型

#include <stdio.h>
int main() {
    unsigned int n;
    while (scanf("%u", &n) != EOF) {
        unsigned int ans = n * (n + 1) / 2; // (1)
        printf("%u\n\n", ans);
    }
    return 0;
}
  • ( 1 ) (1) (1) 由於無符號整型的范圍為 [ 0 , 2 32 − 1 ] [0, 2^{32}-1] [0,2321],當 n = 65535 n = 65535 n=65535 時,有:
  • n × ( n + 1 ) = 2 32 − 2 16 < 2 32 − 1 n \times (n+1) = 2^{32} -2^{16} \lt 2^{32}-1 n×(n+1)=232216<2321
  • 所以不需要擔心溢出問題。

5、正確解法4:64位整型

#include <stdio.h>
int main() {
    long long n;
    while (scanf("%lld", &n) != EOF) {
        long long ans = n * (n + 1) / 2;  // (1)
        printf("%lld\n\n", ans);
    }
    return 0;
}
  • ( 1 ) (1) (1) long long是C語言中的64位整型,范圍比int大了一個平方量級,在 [ − 2 63 , 2 63 − 1 ] [-2^{63}, 2^{63}-1] [263,2631],所以也是能夠涵蓋 n n n 最大的情況的。

🙉自律使你自由!🙉

C語言免費動漫教程,和我一起打卡!
🌞《光天化日學C語言》🌞

LeetCode 太難?先看簡單題!
🧡《C語言入門100例》🧡

數據結構難?不存在的!
🌳《畫解數據結構》🌳

閉關刷 LeetCode,劍指大廠Offer!
🌌《算法入門指引》🌌

LeetCode 太簡單?算法學起來!
💜《夜深人靜寫算法》💜


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM