avatar
Siz Long

My name is Siz. I am a computer science graduate student specializing in backend development with Golang and Python, seeking opportunities in innovative tech projects. My personal website is me.longsizhuo.com .Connect with me on LinkedIn: https://www.linkedin.com/in/longsizhuo/.

  • Resume
  • Archives
  • Categories
  • Photos
  • Music



{{ date }}

{{ time }}

avatar
Siz Long

My name is Siz. I am a computer science graduate student specializing in backend development with Golang and Python, seeking opportunities in innovative tech projects. My personal website is me.longsizhuo.com .Connect with me on LinkedIn: https://www.linkedin.com/in/longsizhuo/.

  • 主页
  • Resume
  • Archives
  • Categories
  • Photos
  • Music

9021_TUT_7_25T1

  2025-04-20
字数统计: 1.4k字   |   阅读时长: 9min

Exercise 1

what is yield in python?

The yield keyword in Python is used to create generator functions. A generator function is a special kind of function that returns a generator iterator, which can be used to iterate over a sequence of values. Unlike a regular function that returns a single value using the return statement, a generator function can yield multiple values, one at a time, pausing its state between each one.

How it works?

When a generator function is called, it doesn’t execute its code immediately. Instead, it returns a generator object that can be iterated over. Each time you request the next item from the generator (using next() or a loop), the generator function resumes execution from where it last left off, runs until it hits a yield statement, and yields the value to the caller.

Problem Description

First, we need to understand the requirements of the problem:

Input: A list of integers L, such as [n1, n2, n3, …]. Output:

  • Output n1 lines of rank 1 X, which means X without indentation.
  • Between each pair of rank 1 X, output n2 lines of rank 2 X, which are indented with one tab (\t).
  • Between each pair of rank 2 X, output n3 lines of rank 3 X, which are indented with two tabs.
  • Continue this pattern until all elements in the list L have been processed.

My solution

1
2
3
4
5
6
7
8
9
10
11
12
13
def f1():
while True:
print(" /\\")
print("/ \\")
yield
print("----")
yield
print("\\ /")
print(" \\/")
yield
print(" ||")
print(" ||")
yield

Standard solution

1
2
3
4
5
6
7
8
9
10
def f1():
while True:
print(' /\\\n/ \\')
yield
print('----')
yield
print('\\ /\n \\/')
yield
print(' ||\n ||')
yield

Exercise 2

Problem Description

1
2
3
4
5
6
7
Line:   [1,   3,   3,   1]
| | |
i=0 i=1 i=2
↓ ↓ ↓
Calculate:1+3 3+3 3+1
↓ ↓ ↓
Results: 4 6 4

My solution

1
2
3
4
5
6
7
def f2():
# initialized
row = [1]
while True:
yield row
# row[i] + row[i + 1] is the expression in list comprehension
row = [1] + [row[i] + row[i + 1] for i in range(len(row)-1)] + [1]

Standard solution

1
2
3
4
5
6
def f2():
L = [1]
yield L
while True:
L = [1] + [L[i] + L[i + 1] for i in range(len(L) - 1)] + [1]
yield L

Exercise 3

Problem Description

First, we need to understand the requirements of the problem:

Input: A list of integers L, such as [n1, n2, n3, …]. Output:

  • Output n1 lines of rank 1 X, which means X without indentation.
  • Between each pair of rank 1 X, output n2 lines of rank 2 X, which are indented with one tab (\t).
  • Between each pair of rank 2 X, output n3 lines of rank 3 X, which are indented with two tabs.
  • Continue this pattern until all elements in the list L have been processed.

My solution

1
2
3
4
5
6
7
8
9
10
def f3(L):
def helper(L, rank):
if not L:
return
n = L[0]
for i in range(n):
print('\t' * rank + 'X')
if i < n - 1:
helper(L[1:], rank + 1)
helper(L, 0)

We need to write a recursive function to handle this nested structure. Here is the implementation idea:

Define a helper function helper(L, rank), where:

  • L is the list currently being processed.
  • rank is the current level (indentation level).

In the helper function:

  1. If the list is empty, return directly.
  2. Get the number of lines to output at the current level, n = L[0].
  3. Use a loop to output n lines of the current level’s X, with each line having the corresponding number of tab characters (\t) based on the rank.
  4. After each output, if it is not the last element and it is not the last line, recursively call helper to handle the next level of X lines.

Standard solution

1
2
3
4
5
6
7
8
9
10
11
12

def f3(L):
_f3(L, 0)

def _f3(L, n):
if n == len(L):
return
for _ in range(L[n] - 1):
print('\t' * n, 'X', sep='')
_f3(L, n + 1)
if L[n]:
print('\t' * n, 'X', sep='')

Exercise 4

Problem Description

Objective: Given a list L of integers, we need to:

  • Break it down into sublists of equal length, where the length is maximal.
  • This process is recursive: each sublist is further broken down in the same manner.
  • The original list should be preserved (i.e., not modified).

Key Points:

  • We aim to split the list into the largest possible sublists of equal length (greater than 1) that evenly divide the length of the list.
  • The recursion continues until a sublist cannot be broken down further (i.e., its length cannot be divided into equal parts greater than 1).

My solution

1
2
3
4
5
6
7
8
9
10
11
from math import sqrt

def f4(L):
n = len(L)
# Try to find the maximal sublist length greater than 1
for sublist_length in range(n // 2, 1, -1):
if n % sublist_length == 0:
# Split the list into sublists
sublists = [f4(L[i:i + sublist_length]) for i in range(0, n, sublist_length)]
return sublists
return L.copy()

Standard solution

1
2
3
4
5
6
7

def f4(L):
for n in range(2, round(sqrt(len(L))) + 1):
if len(L) % n == 0:
w = len(L) // n
return [f4(L[i : i + w]) for i in range(0, len(L), w)]
return list(L)

Exercise 5

Problem Description

Objective: Given a special list L (a list whose elements are integers or other special lists), we need to return a dictionary where:

Keys are tuples of indices (i_1, i_2, ..., i_n).
Values are integers e.
The keys represent the path of indices to reach an integer e within the nested list structure.

Interpretation:

  • If the key is (i_1,), then L[i_1] is an integer e.
  • If the key is (i_1, i_2), then L[i_1][i_2] is an integer e.
  • If the key is (i_1, i_2, i_3), then L[i_1][i_2][i_3] is an integer e.
  • And so on.

Constraints:

We need to handle any depth of nesting.
Use isinstance() to check if an element is an integer or a list.
The original list should not be modified.

My solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def f5(L):
result = {}
def helper(sublist, path):
for index, element in enumerate(sublist):
current_path = path + (index,)
if isinstance(element, int):
result[current_path] = element
elif isinstance(element, list):
helper(element, current_path)
else:
# If there are other types, you might want to handle them or raise an error
pass
helper(L, ())
return result

Standard solution

1
2
3
4
5
6
7
8
9
10
def f5(L):
D = {}
for i in range(len(L)):
if isinstance(L[i], int):
D[(i,)] = L[i]
else:
E = f5(L[i])
for s in E:
D[i, *s] = E[s]
return D

Difference between my solution and the standard solution

My Solution

  • Uses a Helper Function (helper): Your solution defines an inner function helper(sublist, path) to handle the recursion.
  • Explicit Path Passing: The path variable, representing the current position in the nested list, is explicitly passed and updated at each recursive call.
  • State Encapsulation: By using a nested function, the state (path and result) is encapsulated within the f5 function’s scope.
    Standard Solution
  • Direct Recursion: The standard solution directly calls f5(L[i]) recursively without using a helper function.
  • Implicit Path Construction: It constructs the path by combining the current index i with the indices from the recursive call (s) using tuple unpacking.
  • Dictionary Merging: After each recursive call, it merges the returned dictionary E into the current dictionary D.

Exercise 6

Problem Description

The given problem is not strictly about the Fibonacci sequence, but it generalizes similar principles to a broader set of recursive relationships. In the problem, you’re asked to compute the n-th term of a series, where the series is defined by some initial terms (first_terms) and a set of recurrence factors (factors). The Fibonacci sequence is a special case of such a recurrence relation, where each term is the sum of the two preceding terms.

My solution(Wrong)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

def f6(first_terms, factors, n):
k = len(first_terms) - 1
sequence = first_terms.copy()

# If n is within the initial terms, return it directly
if n <= k:
return sequence[n]

# Compute terms iteratively up to n
for i in range(k + 1, n + 1):
xi = 0
for s in range(k + 1):
xi += factors[s] * sequence[i - k - 1 + s]
sequence.append(xi)

return sequence[n]

Standard solution

1
2
3
4
5
6
7
8
9
10
11
from collections import defaultdict

def f6(n):
_f6(n)

def _f6(n, nb_of_tabs=0, nb_of_calls=defaultdict(int)):
nb_of_calls[n] += 1
print('\t' * nb_of_tabs, f'Computation nb {nb_of_calls[n]} of fibo({n})...', sep='')
fibo_n = n if n in {0, 1} else _f6(n - 1, nb_of_tabs + 1) + _f6(n - 2, nb_of_tabs + 1)
print('\t' * nb_of_tabs, f'... computed as {fibo_n}', sep='')
return fibo_n
  • Python
  • Answer
  • 9021
  • Tutorial

show all >>

Kuaishou front-end campus recruitment eight-part essay review roadmap

The front-end interview of domestic large companies focuses on the examination of basic eight-part essay knowledge. Even if you don’t like this mode in form, you have to prepare, otherwise it will be difficult to enter the large company. Given that there are only two days left before the interview, it is necessary to review the frequently tested knowledge points efficiently and in a targeted manner. The following divides the knowledge points into modules, and provides a list of key points, in-depth mastering suggestions, review order and reference materials.

1. Classification and key list of important knowledge points

It is recommended to sort out the knowledge into the following modules, and each module lists common “eight-part essay” test points for quick review:

1. HTML and CSS Basics

  • CSS box model: Understand the composition of the box (content, padding, border, margin) and its impact on element size.
  • CSS selector priority: Master the priority calculation rules when styles conflict (inheritance < element inline style < !important, etc.).
  • Elements horizontally and vertically centered: Be familiar with a variety of centering layout methods, such as using positioning + margin, positioning + transform, or using Flexbox to achieve centering.
  • Rearrangement and redrawing: Understand the difference and triggering conditions between rearrangement (reflow) and redrawing. Rearrangement is caused by layout changes (such as size and position changes), and the layout needs to be recalculated; redrawing is a change in appearance style but the layout remains unchanged, only the pixels are redrawn.

2. JavaScript and ES6

  • Prototype and prototype chain: Understand the mechanism of JavaScript prototype chain to achieve inheritance. The prototype is essentially an object used to share properties and methods; instances are linked to the constructor’s prototype through __proto__ to implement property lookup.
  • Closure: Master the concept and function of closure. A closure is a function that can access variables in the scope of another function, often created by creating another function inside a function. Closures can be used to encapsulate private variables and avoid global variable pollution.
  • this binding: Understand the pointing rules of this in different scenarios (functions point to the global object when called directly; methods point to the object that calls it; constructor calls point to the new instance; call/apply can explicitly change this). Note that arrow functions do not have their own this and will capture the outer this.
  • Event loop and asynchrony: Understand the JavaScript Event Loop (Event Loop) mechanism of the browser, and distinguish between macrotasks and microtasks. JavaScript uses a single thread to run, and the event loop continuously checks and executes tasks in the task queue. Master the execution order of asynchronous mechanisms such as setTimeout, Promise, async/await, etc.
  • ES6+ New Features: Be familiar with common ES6/ES7 features and usage, such as the scope difference between let/const, arrow function binding lexical this, Set/Map container types, Promise and async/await to achieve asynchrony, class syntax sugar and inheritance, etc. These features are often high-frequency interview topics, and you should be able to explain their uses and advantages and disadvantages.

3. Browser Principles

  • Browser Rendering Process: Understand the basic process from HTML input to page rendering: build DOM tree -> build CSSOM tree -> merge to generate rendering tree -> layout (Reflow) -> painting (Paint). Know how CSS affects rendering (such as external CSS loading will block rendering tree construction), etc.
  • Browser Multi-Process Architecture: Understand the process division of modern browsers (such as browser main process, rendering process, network process, etc.) and the difference between process and thread. Processes have independent memory space, and crashes do not affect each other; threads are execution units within processes and share memory. Understand that the rendering process in the Chrome browser is multi-threaded (including GUI rendering thread, JS engine thread, etc.).
  • DOM event mechanism: Master the three stages of the browser event flow: capture → target → bubbling. Event capture propagates downward from the ancestor node, and event bubbling propagates upward from the target. Learn how to use the third parameter of addEventListener to control the binding phase, and use stopPropagation() to stop bubbling.
  • Script loading strategy: Understand the difference between the async and defer attributes in the <script> tag: when there is no attribute, the browser parses and executes the script synchronously; async means that the script is downloaded asynchronously and executed immediately after the download is completed (not guaranteed in order); defer means that the script is downloaded asynchronously but delayed until the HTML parsing is completed and then executed in order.
  • Browser storage: Understand the differences between several front-end storage methods: Cookies, localStorage, sessionStorage, IndexedDB, etc. For example, cookies are automatically included in requests and are small in size and are mostly used to save session identifiers. localStorage has a large capacity and persists. sessionStorage is cleared after the session ends.
  • Browser Cache Mechanism: Master HTTP cache strategies, such as the principles of Strong Cache (Expires/Cache-Control) and Negotiation Cache (Last-Modified/Etag). Be able to explain how the browser determines that the cache is valid and returns 304.

4. Network and Protocol

  • Differences between GET and POST: Clarify the differences in semantics and implementation between HTTP GET and POST. For example: GET is generally used to obtain resources, and parameters are attached to the URL and have a length limit; POST is used to submit data, which is placed in the request body and has no length limit; GET requests can be cached by the browser while POST will not be automatically cached; POST is relatively safer in terms of security, etc.
  • HTTP1.1 and HTTP2: Understand the main improvements of HTTP/1.1 compared to HTTP/2. For example, HTTP/2 uses binary framing transmission, supports multiplexing (one connection concurrently multiple requests, no head-of-line blocking HTTP layer, but head-of-line blocking still exists at the TCP layer), header compression, etc. Know the further optimization of HTTP/3 based on QUIC (can be briefly mentioned).
  • Network communication basics: Master the basic process of TCP three-way handshake and four-way handshake, and understand the role of TLS/SSL handshake in HTTPS. Although it is not a pure front-end scope, the interview may involve simple questions and answers.
  • Other protocols/mechanisms: Understand the difference between WebSocket and HTTP (full-duplex communication, handshake upgrade protocol, etc.) and common application scenarios. Familiar with the meaning of HTTP status code (especially 2xx, 3xx redirection, 4xx client error such as 401/403/404, 5xx server error). These are prepared for any eventuality and can be mentioned in passing when answering HTTP-related questions.
  • Request process and optimization: Familiar with the entire process from entering the URL in the browser address bar to loading the page, including DNS resolution, TCP connection, sending requests, receiving responses, and browser rendering. Be able to combine cache, CDN, etc. to explain methods to improve network loading performance (such as enabling CDN nearby access, reducing request size, etc.).

5. Security

  • XSS Cross-site Scripting Attack: Understand the principle of XSS - attackers inject malicious script code into the target website and execute it on other users’ browsers, thereby stealing user data (such as cookies), etc. Master common preventive measures: Input filtering and escaping (HTML escape special characters for user input), Content Security Policy (CSP), sensitive cookie settings HttpOnly to prevent script reading, etc.
  • CSRF Cross-site Request Forgery: Understand the attack mechanism of CSRF - using the user’s logged-in status to send malicious requests in the user’s name without the user’s knowledge. Defense methods include: requesting to attach anti-counterfeiting random tokens (tokens) and verifying, using SameSite strict cookies, adding verification code confirmation to key operations, etc.
  • Same-Origin Policy and Cross-Domain: Understand the restrictions of the browser’s Same-Origin Policy on JavaScript, and know that same-origin means the same protocol, domain name, and port. Master several cross-domain solutions: such as setting CORS response headers on the server to allow cross-domain resource sharing, JSONP using <script> without same-origin restrictions (only supports GET), and reverse proxy forwarding mechanisms.
  • Other security: Understand ClickJacking protection (disabling iframe nesting through X-Frame-Options), and how the front end can prevent crashes caused by malicious input (such as regular denial of service ReDoS), etc. When answering security questions, you can combine the security reinforcement measures encountered in actual projects to reflect your experience.

6. Front-end framework principles (mainly React)

  • Virtual DOM and Diff: Understand the role of virtual DOM: add an abstract DOM tree cache between JS and real DOM, and compare the new and old virtual DOMs through the Diff algorithm to efficiently update the real DOM and avoid unnecessary DOM operations. Know React’s Diff strategy (same-layer comparison, list nodes require unique key, etc.) and the performance improvement it brings.

  • React Fiber Architecture: Understand the reasons and principles for the introduction of Fiber in React16. React15’s coordinator uses recursive processing of component trees, and updates cannot be interrupted. Long updates will block the UI. Fiber splits update tasks into small, interruptible units (linked list form), uses browser idle time to execute, avoids long tasks blocking rendering, and implements interruptible scheduling and rendering.

  • React Lifecycle: Familiar with the various stages of the React component lifecycle (Mounting -> Updating -> Unmounting) and the main lifecycle methods. For example, componentDidMount/Update/WillUnmount, etc., understand their calling order and typical uses. At the same time, understand the Hooks equivalent writing of function components (such as the corresponding part of the lifecycle of useEffect).

  • Synthetic Event Mechanism: Understand how the React synthetic event model works. React does not bind event handlers directly to DOM nodes, but uses an event delegation mechanism to uniformly bind them to the root node (document), which can improve performance and achieve consistent event behavior across browsers. Clarify the difference between this mechanism and native DOM events, such as event pools, syntheticEvent objects, etc.

  • Error Boundary: Know how to capture errors in React, such as using componentDidCatch or getDerivedStateFromError to define Error Boundary components to uniformly handle runtime errors of subcomponents to prevent errors from causing the entire application to crash (this can be mentioned briefly).

  • Basic understanding of Vue: Although Vue is not actually used, you should have a rough understanding of the core principles of Vue to deal with comparison problems. For example: Vue achieves responsiveness through data hijacking (Vue2 uses Object.defineProperty, Vue3 uses Proxy); Vue templates will parse instructions and interpolation during the compilation phase, generate virtual DOM and update; the difference between Vue’s two-way data binding (v-model) and React’s one-way data flow, etc.

  • React vs Vue: can compare the similarities and differences of the two frameworks. Similarities: Both use component-based, data-driven views, use virtual DOM+Diff algorithm to optimize updates, and have supporting routing and state management solutions. Differences:

  • Core concept: Vue is a progressive framework, the core of which is responsive MVVM data binding; React is a library for building UIs, emphasizing one-way data flow and functional UI.

  • Syntax style: Vue recommends SFC single-file components (template+script+style, intuitive and easy to use), and also supports directive syntax and JSX; React forces the use of JSX, writing HTML and CSS in JavaScript with JSX/Inline Style (“All in JS”).

  • Data update mechanism: Vue automatically tracks dependencies and updates the DOM efficiently when data changes (with a lot of built-in optimizations and syntax sugar), so developers don’t need to handle it manually; React needs to trigger updates through setState, and may need to deal with performance issues (such as manually using shouldComponentUpdate or Memo to optimize), and many functions (such as global state management) rely on community libraries.

  • Other: Vue2 is more declarative programming, and Vue3 began to introduce a combination API close to React’s Hooks idea; Vue has built-in two-way binding to facilitate form processing, while React defaults to one-way data flow, and you need to write event processing yourself to update the state, etc. In general, both have their advantages and disadvantages, and you need to choose according to the scenario. If you talk about it during the interview, you can first point out their common ideas, and then explain them with the above differences.

7. Performance optimization

  • Web page performance indicators: Understand the concepts of common page performance indicators such as first screen rendering time, white screen time, TTFB, FPS, etc., so as to facilitate discussion of performance experience with the interviewer. But more importantly, master the optimization methods.
  • Resource loading optimization: Master the principle of lazy loading of images (delayed loading of off-screen images, users scroll to the visible area and then set the real src to trigger loading) and implementation methods (such as <img> using data-src + listening to scroll events or IntersectionObserver). Understand code segmentation and on-demand loading, and use Webpack and other methods to split bundles by route or component to reduce the size of first screen resources.
  • Function throttling and debounce: Understand the application of throttling (throttle) and debounce (debounce) technologies in the optimization of high-frequency events such as scrolling and input. Throttling is to let the function execute at a fixed frequency under continuous triggering (at most once in n seconds); debounce is to merge multiple triggers into the last execution (wait for n seconds after the trigger is completed). Can give examples of usage scenarios, such as throttling for scrolling event processing, anti-shake for input box search to prevent continuous requests, etc.
  • Reduce reflow and redraw: Optimize DOM operations to minimize the number of Layout/Reflow and the scope of impact. Specific measures include: avoid frequent direct style modifications, prioritize batch modifications or use class switching; avoid frequent insertion and deletion of nodes in the DOM tree, if necessary, you can first display:none the element (trigger a reflow first), and then display it after the operation is completed; use transform or opacity to trigger the synthesis layer for large animations to avoid repeated reflow; avoid using table layout, etc.
  • Cache and resource optimization: Use browser cache (strong cache/negotiated cache) to reduce requests; enable Gzip/Brotli compression to transmit resources; place commonly used static resources on CDN to increase access speed; use browser offline storage (such as Service Worker cache) to improve revisit performance, etc.
  • Build packaging optimization: Optimize front-end code performance through build tools. Typical methods include: code compression and obfuscation (reducing file size), Tree-Shaking (tree-shaking optimization to remove unreferenced code), SplitChunks code splitting, lazy loading by route, extracting public libraries, enabling package cache, etc. In addition, using production mode to build and remove development and debugging code, and reasonably configuring the optimization option of webpack are also common interview points.
  • Performance monitoring: Familiar with basic performance monitoring methods, such as using Chrome DevTools Performance to record and analyze frame rate and call stack, and using Lighthouse for performance scoring. Although the interview may not ask about the use of tools in detail, it will be a plus if you can show your ideas for monitoring and locating performance problems.

8. Front-end engineering and modularization

  • Version control (Git): Understand common Git commands (such as clone, branch, add, commit, push, etc.) and team collaboration processes. In particular, you need to understand the difference between merge and rebase in the branch merge strategy: merge will generate a new merge commit, retaining the historical branch fork; rebase will replay the branch commits sequentially at the top of the target branch without generating additional fork points. Large companies may pay attention to your understanding of team collaboration and code management.
  • Build Tool (Webpack): Frequently tested Webpack principles and configurations. You need to know the difference between Loader and Plugin: Loader is essentially a file converter, used to process non-JS resources and convert them into modular JS; Plugin is hooked in the Webpack life cycle to extend Webpack functions, such as packaging optimization. Be familiar with several typical Loaders/Plugins (such as babel-loader converting ES6 to ES5, file-loader processing images, HtmlWebpackPlugin automatically generating HTML, etc.).
  • Webpack Hot Update HMR: Understand the principle of Webpack Hot Module Replacement. That is, in development mode, Webpack-dev-server establishes a WebSocket connection. When a source code change is detected, the new module code is pushed to the browser through WebSocket and replaces the old module without refreshing the entire page. Can briefly describe the process and benefits of HMR (preserving application status and improving development efficiency).
  • Babel translation: Master the process of Babel converting ES6+ code to backward compatible JS. Including: Parsing (Parsing)-parse the source code into an abstract syntax tree (AST); Transforming (Transforming)-transform AST nodes according to the configured plug-in presets (such as arrow functions to ordinary functions, etc.); Generating (Generating)-regenerate JS code according to the converted AST. This process reflects the compilation mechanism of front-end engineering, and senior interviews may ask in depth.
  • Lint and unit testing: Understand code quality tools such as ESLint rule customization, understand how to encapsulate custom ESLint plug-ins (know its basic mechanism of traversing AST, which can be briefly mentioned). Familiar with the meaning of front-end unit testing and common frameworks (Jest, etc.), although it is not necessary to ask about the implementation in detail, it is beneficial to demonstrate engineering literacy.
  • Modularization specification: Understand the differences between mainstream JS modularization solutions. CommonJS module is used in Node.js, and uses synchronous require() to load the module, which loads the module’s output object (value copy); ES6 Module standard determines module dependencies and output interfaces at compile time, supports import/export, is static analysis loading, and exports read-only references. Therefore, ES6 modules can perform Tree-shaking optimization of unused code. You can also learn about historical solutions such as AMD and CMD and why they are gradually replaced by ES6 modules (AMD asynchronous loading is suitable for browsers but the syntax is not intuitive, and the ES6 module syntax is more concise and unified).

The above classification almost covers the high-frequency knowledge points of front-end interviews. It is recommended to go through the list one by one during the review, and check the unfamiliar concepts in time to deepen your understanding.

2. Key content that needs to be mastered in depth (combined with preferences and background)

Interview preparation time is limited, so you should focus on it. According to Kuaishou interview preferences and candidates’ own background, the following modules and knowledge are worth investing more effort in in-depth mastery:

  • React principles and framework comparison: As a candidate’s strength, React-related principles need to be mastered in depth so that you can show a deeper understanding than ordinary candidates in the interview. Kuaishou and other large companies often ask in-depth questions about framework implementation, such as virtual DOM, Diff algorithm, or ask you to compare the differences between React and Vue. Make full use of your practical experience with React and study its underlying mechanisms (such as Fiber architecture, synthetic events, etc.) in depth to make your answer more brilliant. Since you have not used Vue, you should also understand the basic concepts of Vue in advance to prevent comparison questions - the depth does not need to be too great, but you need to ensure that you do not leave a blank when answering.

  • Performance optimization (especially the first screen and interactive performance): This is an area where you have practical experience, and it is also an aspect that platforms with a huge number of users like Kuaishou attach great importance to. In the interview, you may be asked how to optimize a performance bottleneck scenario, such as how to improve the slow loading of the first screen or make a product smoother in a weak network environment. You should have a deep understanding of performance optimization techniques at all levels (loading, rendering, encoding, etc.) and be prepared to combine case studies with your own projects. Kuaishou directly asked candidates what performance optimizations they made in their mini-programs during the interview, which shows how much they value this. Make full use of your practice in performance tuning, sort out a set of systematic optimization ideas, and talk about it in the interview will be a big plus.

  • Browser principles and JS basics: These are the foundation of front-end interviews, and often determine the interviewer’s evaluation of your depth and breadth. Interviews at large companies like to extend the basic concepts to ask tricky questions (for example, using examples to examine the event loop instead of asking about the concept directly). In view of this, you need to be familiar with the basics (event loop, scope closure, prototype chain, DOM/CSS mechanism, etc.) and understand them thoroughly. Combined with your full-stack background, you can have a deeper understanding of some browser mechanisms (such as the relationship between the rendering thread and the JS thread), which will make your answers more convincing. When reviewing, focus on understanding the principles instead of memorizing the conclusions, so that no matter how the interview changes, you can handle it with ease.

  • Security knowledge: You have practical development experience in security, which is your advantage. Front-end interviews at large companies often ask security questions such as XSS/CSRF prevention measures, and many fresh graduates are weak in this area. If you can give a rich and detailed answer (such as explaining output encoding, various means of defending CSRF and combining back-end verification mechanisms, etc.), it will surely impress the interviewer. Since you have a back-end development background, you can explain the security solution from a full-stack perspective (such as how the server cooperates with protection), reflecting a comprehensive security awareness. It is recommended to focus on memorizing the causes and defense strategies of common Web vulnerabilities, and appropriately demonstrate your security sensitivity in the interview.

  • Engineering and project practice: Considering that your background involves back-end and deployment technologies such as Go and Docker, Kuaishou interviewers may be interested in your engineering thinking and full-stack capabilities. Although campus recruitment front-end positions do not necessarily test back-end knowledge in depth, front-end engineering is a common test point. You should have a deep understanding of the principles of the build tool chain (Webpack, Babel, etc.) to deal with difficult questions. For example, Kuaishou has interviewed questions about Babel transcoding process, custom ESLint plug-ins and other engineering-oriented questions. You can use your own learning and project experience to give in-depth answers to these questions. At the same time, you can prepare some insights on topics such as how to use Docker to deploy front-end applications and front-end and back-end collaboration optimization, and show your maturity in engineering during the free question session or resume discussion.

Focusing on the above points will maximize your advantages. In these two days, spend more time digging into the details of the above content and organizing the output framework. At the same time, you don’t need to make special preparations for algorithms and handwritten code questions (the requirements of the questions have been clearly stated and will not be tested), and focus your energy on the knowledge section. For Vue framework details, you can just try them briefly and have a good idea of ​​them: after all, it is difficult to master Vue from scratch in two days. It is better to use the time to strengthen React and common principles, and be prepared to show your willingness to quickly learn new frameworks.

3. Review order and time arrangement (two-day plan)

Overall idea: First consolidate the foundation and then sprint to the key points, and allocate time reasonably to ensure breadth coverage while highlighting the depth of key points. In the only two days, divide each day into several time periods and review different modules alternately to maintain efficiency. The specific arrangement is as follows:

Day 1 Morning: Basic consolidation (HTML/CSS & JavaScript)
Use the clearest time in the morning of the first day to quickly review the basic knowledge of the front-end. First spend about 2 hours to sort out the HTML/CSS basics: review the box model, layout techniques, common style problems, etc. (You can quickly browse the relevant questions and answers by browsing the summary of front-end interview questions). Then spend about 2 hours to strengthen the JavaScript core: focus on high-frequency test points such as scope, closure, prototype chain, this, event loop, etc., and combine code snippets to deepen understanding. If you encounter vague concepts, check the MDN documents or notes in time to clarify the principles. This basic module review is not only a preparation for individual test points, but also a prerequisite for subsequent understanding of advanced topics (such as framework principles).

Day 1 Afternoon: Framework Principles (React as the main focus, Vue as a side)
In the afternoon, when you are energetic, focus on React working principles. Spend about 1 hour to sort out knowledge points such as React life cycle, state update mechanism, synthetic event model, etc. It is recommended to summarize while drawing a structure diagram (such as the React Fiber architecture diagram). Next, focus on Virtual DOM & Diff algorithm for about 1 hour: you can find information to read the outline of React Diff principle and practice the virtual DOM diff process on paper. Then spend about 0.5~1 hour to understand Vue core principles: focus on clarifying the implementation of responsive data binding and virtual DOM in Vue, and take a look at the name of Vue life cycle in case you are asked. Finally, leave about 0.5 hours Compare React and Vue: summarize a list of differences between the two (you can directly compare the similarities/differences sorted out before) so that the interview answers are organized. After completing this afternoon review, you will be confident in answering framework questions.

Day 1 Evening: Network and Security
At night, attention is slightly reduced, suitable for reading and memory review. First, spend about 1 hour browsing Computer Network related knowledge: focus on the key points of HTTP protocol (methods, status codes, caching mechanisms, HTTP2 features, etc.), and you can use notes or question banks to quickly test yourself in the form of QA. Some places that need to be remembered (such as the meaning of HTTP status codes) can be consolidated by quickly reciting + memorizing the key points. Next, spend about 1 hour to concentrate on studying the Web Security module: It is recommended to read the typical cases and protection solution blogs of XSS and CSRF to deepen your impression, and then close the materials and try to repeat the complete attack process and defense measures by yourself. If you have enough time, you can also learn about common browser security strategies (such as same-origin strategy, CORS principle, etc.). Day1 Before the end of the night, you can briefly review the content of the day, deepen your memory, and list the points that are still in doubt, ready to solve the next day.

Day 2 Morning: Performance Optimization Special
The next morning, you will be energetic and devote yourself to the review of the Performance Optimization module. It takes about 1 hour to learn Page loading optimization: including resource packaging optimization

Optimization strategies (file splitting, lazy loading), cache utilization, etc. You can get the key points by reading performance optimization best practices articles and compare them with your own projects to see what methods are used. Then spend about 1 hour reviewing Rendering performance optimization: focus on how to reduce reflow and redrawing, improve animation fluency, etc. If necessary, go over the browser rendering principle again to understand the reasons behind the optimization. Next, spend about 0.5 hours reviewing Algorithm optimization and memory optimization (although algorithm questions are not tested, understanding the time-consuming DOM operations and JS big data processing optimization such as Web Worker can prevent the interview from discussing related scenarios). Finally, leave about 0.5 hours to sort out a set of performance optimization plan lists: write down keywords and measures according to the categories of loading optimization, rendering optimization, and code optimization. This list is not only a summary of knowledge points, but can also be used as an answer outline when the interview asks about performance.

Day 2 Noon/early afternoon: Engineering and modularization
Use the time period at noon or early afternoon (about 1.5 hours) to review Front-end engineering related content. First, review the core concepts and common configuration items of Webpack, focusing on understanding the Loader/Plugin mechanism and the build process. You can quickly browse the Q&A based on the Webpack interview questions on the Internet. Then spend 0.5 hours to read the relevant knowledge of Babel/Polyfill, and make sure you can explain the Babel transcoding process and commonly used translation plug-ins. Next, spend 0.5 hours to get familiar with Git process and tools: focus on branch management and multi-person conflict resolution in team collaboration, clarify the difference between rebase and merge, and concepts such as semantic submission, in case the interviewer mentions it by the way. Spend another 0.5 hours to review Modular Evolution: carefully read the differences between CommonJS and ES6 Module, remember the key differences (synchronous/asynchronous, value copy/reference, etc.), and imagine how to answer concisely if asked. After this intermediate period of time, your knowledge of engineering will be systematic, and you will not be vague on related issues.

Day 2 Late afternoon: Comprehensive lectures and simulations
In the last half day, conduct a comprehensive sprint and check for omissions. First, spend about 1 hour to quickly talk through all the previous notes: you can try to recall the key points of each module without taking the test, and then check the list to supplement the forgotten points. This active recall helps to transfer knowledge from short-term memory to long-term memory, which is convenient for the interview the next day. Next, spend about 1 hour to practice simulated question and answer: you can find a set of front-end eight-part essay question banks, randomly extract questions, quickly organize the answers in your mind and orally express them. This step is very important, as it can expose what knowledge you have read but cannot express. For the weak points found, immediately look through the materials to understand them again to ensure that you can express them fluently during the interview. Finally, half an hour, browse common interview summary blogs or question lists, and get some information on popular high-frequency questions (for example, you can take a look at the popular questions about AST recently, but don’t get stuck in new knowledge), so that you have a clear idea. At the end, adjust your mentality and organize the knowledge framework of the paper/mind map version. At this point, you have covered the interview knowledge points to the greatest extent. Try to get enough rest at night to maintain a good mental state for the interview.

*After two days of intensive review, you should have a clear understanding of the core knowledge. If you encounter unprepared questions during the interview, don’t be nervous. Try to analyze and answer them on the spot based on the relevant basic knowledge. Through the efficient review of the above plan, you will face the Kuaishou front-end interview with a more calm and confident attitude. *

IV. References

During the review process, you can use some high-quality question banks and blog posts to efficiently obtain key knowledge and typical questions and answers. The following resources are recommended for reference:

  • GitHub repository: wangwenjie1314/webQd – Front-end high-frequency interview question collection, which organizes the latest frequently tested knowledge points in 2024 according to modules (HTML&CSS, JS, Vue, React, engineering, network, etc.). This repository summarizes excellent articles and interview experiences of netizens, and is an excellent index for brushing questions.

  • Rare Earth Gold Digging “Front-end Eight-part Essay” Series – Special articles by author wakaka378 and others, covering browsers, networks, performance, engineering and other aspects of the eight-part essay key points. For example, the “Essential Eight-part Essays for Front-end Interviews - Browsers” deeply summarizes XSS/CSRF, anti-shake throttling, event mechanisms, etc., and the “Network Related” article summarizes the characteristics of various HTTP versions. These series of articles are concise in language and highlight the key points, which are very suitable for quick review before the exam.

  • CSDN Blog: “The Latest Eight-part Essays for Web Front-end Interviews in 2024” - Comprehensive Front-end Interview Knowledge Encyclopedia. This blog post lists typical questions and reference answers in various fields of the front-end in detail by chapter, from CSS, JS basics to frameworks, performance, and security. The content is updated to the end of 2024, covers a wide range and is accompanied by explanations, which is suitable as a reference for checking knowledge blind spots and learning answer expressions.

  • **Zhihu Column: **《Web front-end interview questions and answers that must be asked in the 2024 interview》 – A comprehensive collection of front-end core test points, covering almost all categories such as JavaScript, CSS, ES6, Vue2/3, React, Node.js, applet, HTTP, TypeScript. Each knowledge point has a detailed answer, which can be used to recite key expressions based on understanding. This type of Zhihu answer collection condenses a lot of interview experience essence and is of high quality.

  • MDN Web Docs (Mozilla Developer Documentation) – Authoritative technical details reference. When encountering vague concepts, the relevant pages on MDN usually provide authoritative and clear explanations. For example, check the “Event Loop” entry to understand the accurate description of the JS runtime model. MDN documents are mostly in English, but the content is credible and detailed, and it is very valuable when looking up specific concepts (such as HTTP cache, CSS properties, etc.).

  • Niuke.com NowCoder Front-end Interview Section – A large number of interview experience posts and real questions discussions that are updated in real time. You can search for “Kuaishou front-end interview experience” to get the real Q&A situations of previous years and understand the types of questions that Kuaishou interviewers like to ask. Using your spare time to read a few highly praised interview experiences will help you master the interview focus and answering skills. However, you should be aware that the content of interview experiences varies greatly, and you should not be affected by other people’s negative experiences.

The above materials are mainly in Chinese, with high information density, which is very suitable for the needs of quick review before the exam. It is recommended to make reasonable use of these resources: Learn knowledge in an outline, and Refer to the expression of excellent answers to optimize your answer method. By combining question bank drills with intensive document reading, you will have a deeper understanding and stronger memory of the eight-part essay test points, be able to handle the interview with ease, and strive to win the Kuaishou front-end campus recruitment offer in one fell swoop!

快手前端校招八股文复习路线图

国内大厂前端面试注重基础八股文知识的考察,即使形式上不喜欢这种模式,也不得不准备,否则很难进入大厂。鉴于距离面试仅剩两天,需要高效、有针对性地复习常考知识点。以下将知识点划分模块,并提供重点清单、深度掌握建议、复习顺序和参考资料。

一、重要知识点分类及重点清单

建议将知识梳理为以下几个模块,每个模块列出常见的“八股文”考点以备速览复习:

1. HTML 与 CSS 基础

  • CSS盒模型:了解盒子的组成(内容content、内边距padding、边框border、外边距margin)及其对元素尺寸的影响。
  • CSS选择器优先级:掌握样式冲突时优先级计算规则(继承 < 元素内联样式 < !important 等)。
  • 元素水平垂直居中:熟悉多种居中布局方法,如利用定位 + margin、定位 + transform,或使用弹性盒(Flexbox)等实现居中。
  • 重排与重绘:理解重排(回流)和重绘的区别及触发条件。重排是布局改变(如尺寸、位置变化)引起,需要重新计算布局;重绘是外观样式改变但布局未变,只重新绘制像素。

2. JavaScript 与 ES6

  • 原型与原型链:理解 JavaScript 原型链实现继承的机制。原型本质是一个对象,用于共享属性和方法;实例通过 __proto__ 链接至构造函数的 prototype 实现属性查找。
  • 闭包:掌握闭包概念及作用。闭包是指能够访问另一个函数作用域中变量的函数,常通过在一个函数内部创建另一个函数产生。闭包可用于封装私有变量、避免全局变量污染等。
  • this 绑定:搞清 this 在不同场景下的指向规则(函数直接调用时指向全局对象;方法调用指向调用它的对象;构造函数调用指向新实例;call/apply 可以显式改变 this)。注意箭头函数没有自身 this,会捕获外层 this。
  • 事件循环与异步:理解浏览器的 JavaScript 事件循环 (Event Loop) 机制,区分宏任务(macrotask)和微任务(microtask)。JavaScript 采用单线程运行,通过事件循环不断检查并执行任务队列中的任务。掌握如 setTimeout、Promise、async/await 等异步机制的执行顺序。
  • ES6+ 新特性:熟悉常见ES6/ES7特性及用法,例如 let/const 的作用域区别、箭头函数绑定词法this、Set/Map容器类型、Promise 及 async/await 实现异步、类(Class)语法糖及继承等。这些特性往往是面试高频话题,应能说明其用途和优劣。

3. 浏览器原理

  • 浏览器渲染流程:理解从输入HTML到页面呈现的基本过程:构建DOM树 -> 构建CSSOM树 -> 合并生成渲染树 -> 布局(Reflow)-> 绘制(Paint)。知道CSS如何影响渲染(如外部CSS加载会阻塞渲染树构建)等。
  • 浏览器多进程架构:了解现代浏览器的进程划分(如浏览器主进程、渲染进程、网络进程等)以及进程和线程的区别。进程有独立内存空间,崩溃互不影响;线程是进程内执行单元,共享内存。理解Chrome浏览器中渲染进程是多线程的(包含GUI渲染线程、JS引擎线程等)。
  • DOM 事件机制:掌握浏览器事件流的三个阶段:捕获 → 目标 → 冒泡。事件捕获从祖先节点向下传播,事件冒泡则从目标向上传播。了解如何使用addEventListener的第三个参数控制绑定阶段,以及使用stopPropagation()阻止冒泡。
  • 脚本加载策略:理解<script>标签中 async 和 defer 属性的区别:没有属性时浏览器同步解析并执行脚本;async表示脚本异步下载且下载完成后立即执行(不保证按顺序);defer表示脚本异步下载但延迟到HTML解析完成后按顺序执行。
  • 浏览器存储:了解几种前端存储方式:Cookie、localStorage、sessionStorage、IndexedDB 等区别。例如,Cookie会被自动附带在请求中、容量小多用于保存会话标识,localStorage容量较大且持久存在,sessionStorage在会话结束后清空等。
  • 浏览器缓存机制:掌握HTTP缓存策略,如强缓存(Expires/Cache-Control)和协商缓存(Last-Modified/Etag)的原理。能解释浏览器如何判断缓存有效以及返回304的过程。

4. 网络与协议

  • GET 和 POST 区别:明确 HTTP GET 与 POST 在语义和实现上的差异。例如:GET一般用于获取资源,参数附在URL且有长度限制;POST用于提交数据,放在请求体,无长度限制;GET请求可被浏览器缓存而POST不会自动缓存;安全性上POST相对更安全一点等。
  • HTTP1.1 和 HTTP2:了解HTTP/1.1相对于HTTP/2的主要改进点。如HTTP/2采用二进制分帧传输、支持多路复用(一个连接并发多个请求,无队头阻塞HTTP层,但TCP层队头阻塞仍存在)、头部压缩等。知道HTTP/3基于QUIC的进一步优化(可简单提及)。
  • 网络通信基础:掌握TCP三次握手、四次挥手的基本过程,了解TLS/SSL握手在HTTPS中的作用。虽非纯前端范畴,但面试可能涉及简单问答。
  • 其他协议/机制:了解WebSocket相对HTTP的区别(全双工通信、需握手升级协议等)和常见应用场景。熟悉HTTP状态码含义(尤其2xx、3xx重定向、4xx客户端错误如401/403/404、5xx服务器错误)。这些有备无患,回答HTTP相关问题时可顺带提及。
  • 请求过程和优化:熟悉从浏览器地址栏输入URL到页面加载的全过程,包括DNS解析、TCP连接、发送请求、接受响应、浏览器渲染的各个阶段。能够结合缓存、CDN等说明提高网络加载性能的方法(如启用CDN就近访问,减少请求体积等)。

5. 安全

  • XSS 跨站脚本攻击:理解 XSS 的原理——攻击者向目标网站注入恶意脚本代码,使之在其他用户浏览器上执行,从而窃取用户数据(如Cookie)等。掌握常见防范措施:输入过滤和转义(对用户输入进行HTML转义过滤特殊字符)、内容安全策略(CSP)、敏感Cookie设置HttpOnly防止脚本读取等。
  • CSRF 跨站请求伪造:了解 CSRF 的攻击机制——利用用户已登录状态,在用户不知情情况下冒用其身份发送恶意请求。防御方法包括:请求附加**防伪随机令牌(token)**并验证、使用SameSite严格的Cookie、关键操作增加验证码确认等。
  • 同源策略与跨域:理解浏览器同源策略(Same-Origin Policy)对JavaScript的限制,知道同源指协议、域名、端口均相同。掌握几种跨域解决方案:如服务端设置 CORS 响应头允许跨域资源共享、JSONP 利用 <script> 不受同源限制(仅支持GET)、以及反向代理转发等机制。
  • 其他安全:了解点击劫持(ClickJacking)防护(通过X-Frame-Options禁用iframe嵌套),以及前端如何防范恶意输入导致的崩溃(如正则拒绝服务ReDoS)等。安全类问题回答时可以结合实际项目中遇到的安全加固措施来体现经验。

6. 前端框架原理(React 为主)

  • Virtual DOM 与 Diff:理解虚拟DOM的作用:在JS和真实DOM之间加了一层抽象的DOM树缓存,通过Diff算法比较新旧虚拟DOM高效更新真实DOM,避免不必要的DOM操作。知道React的Diff策略(同层对比、列表节点需唯一key等)和其带来的性能提升。

  • React Fiber 架构:掌握React16引入Fiber的原因和原理。React15的协调器采用递归处理组件树,更新不可中断,长时间更新会阻塞UI。Fiber将更新任务拆分为可中断的小单元(链表形式),利用浏览器空闲时间执行,避免长任务阻塞渲染,实现可中断的调度与渲染。

  • React 生命周期:熟悉React类组件生命周期各阶段(挂载Mounting -> 更新Updating -> 卸载Unmounting)及主要生命周期方法。如componentDidMount/Update/WillUnmount等,了解其调用顺序和典型用途。同时了解函数组件的Hooks等价写法(如useEffect对应部分生命周期)。

  • 合成事件机制:了解React合成事件模型的工作方式。React并没有将事件处理器直接绑定在DOM节点上,而是采用事件委托机制**统一绑定在根节点(document)**上,这样可以提高性能并实现跨浏览器一致的事件行为。明晰这一机制与原生DOM事件的区别,如事件池、syntheticEvent对象等。

  • 错误边界:知道React中捕获错误的方式,例如使用componentDidCatch或getDerivedStateFromError定义Error Boundary组件来统一处理子组件的运行时错误,以防止错误导致整个应用崩溃(这一点可以简单提及)。

  • Vue 基础了解:尽管未实际使用Vue,但应粗略了解Vue的核心原理以应对比较类问题。如:Vue通过数据劫持实现响应式(Vue2用Object.defineProperty,Vue3用Proxy);Vue模板会在编译阶段解析指令和插值,生成虚拟DOM并更新;Vue的双向数据绑定(v-model)和React单向数据流的区别等。

  • React vs Vue:能比较两大框架的相同点和差异。相同点:二者都采用组件化、数据驱动视图,都使用虚拟DOM+Diff算法优化更新,也都有配套的路由和状态管理方案。差异:

    • 核心理念:Vue是渐进式框架,核心是响应式的MVVM数据绑定;React是用于构建UI的库,强调单向数据流和函数式 UI。
    • 语法风格:Vue推荐SFC单文件组件(template+script+style,直观易上手),也支持指令语法和JSX;React强制使用JSX,将HTML和CSS以JSX/Inline Style写在JavaScript中(“All in JS”)。
    • 数据更新机制:Vue自动追踪依赖并在数据变化时高效更新DOM(大量内置优化和语法糖),开发者无需手动处理;React需通过setState触发更新,可能需要应对性能问题(例如手动使用shouldComponentUpdate或Memo来优化),很多功能(如全局状态管理)依赖社区库提供。
    • 其他:Vue2偏声明式编程,Vue3开始引入组合式API接近React的Hooks思想;Vue内置双向绑定方便表单处理,而React默认单向数据流,需要自行编写事件处理来更新状态等。总体来说,二者各有优劣,需根据场景选择。面试中谈及时,可以先指出它们的共同理念,再举上述差异点加以阐述。

7. 性能优化

  • 网页性能指标:了解页面性能常用指标如首屏渲染时间、白屏时间、TTFB、FPS等概念,便于和面试官讨论性能体验。但更重要是掌握优化方法。
  • 资源加载优化:掌握图片懒加载原理(延迟加载屏幕外图片,用户滚动到可视区域再设置真实src触发加载)和实现方式(如<img>使用data-src+监听scroll事件或IntersectionObserver)。了解代码分割和按需加载,通过Webpack等实现按路由或组件拆分bundle,减少首屏资源体积。
  • 函数节流与防抖:理解节流 (throttle) 和防抖 (debounce) 技术在滚动、输入等高频事件优化中的应用。节流是让函数在连续触发情况下以固定频率执行(n秒至多执行一次);防抖是将多次触发合并为最后一次执行(等待触发完n秒后执行)。能举例说明使用场景,如节流用于滚动滚动事件处理、防抖用于输入框搜索防止连续请求等。
  • 减少重排重绘:优化DOM操作,尽量减少Layout/Reflow次数和影响范围。具体措施如:避免直接频繁修改样式,优先批量修改或使用class切换;避免在DOM树中频繁插入删除节点,必要时可以先将元素display:none(先触发一次重排),操作完成后再显示;对大型动画使用transform或opacity触发合成层,避免反复回流;避免使用table布局等。
  • 缓存和资源优化:利用浏览器缓存(强缓存/协商缓存)减少请求;开启 Gzip/ Brotli 压缩传输资源;将常用静态资源放置CDN提高访问速度;使用浏览器离线存储(如Service Worker缓存)提升重访性能等。
  • 构建打包优化:通过构建工具优化前端代码性能。典型方法包括:代码压缩混淆(减小文件体积)、Tree-Shaking(摇树优化删除未引用代码)、SplitChunks拆分代码、按路由懒加载、提取公共库、开启打包缓存等。另外,利用生产模式构建去除开发调试代码,合理配置webpack的optimization选项等也是常见面试点。
  • 性能监测:熟悉基本性能监测手段,如使用Chrome DevTools Performance记录分析帧率、调用栈,用Lighthouse进行性能评分等。虽然面试不一定细问工具使用,但体现出对性能问题的监测和定位思路会有加分。

8. 前端工程化与模块化

  • 版本控制 (Git):了解常用的 Git 命令(如clone、branch、add、commit、push等)和团队协作流程。尤其需掌握分支合并策略中 merge 和 rebase 的区别:merge会产生一次新的合并提交,保留分支的历史分叉;rebase则将分支提交在目标分支顶端顺序重放,不产生额外的分叉点。大厂可能关注你对团队协作和代码管理的理解。
  • 构建工具 (Webpack):常考 Webpack 原理及配置。需要清楚 Loader 和 Plugin 的区别:Loader本质是文件转换器,用于处理非JS资源,将其转为可模块化JS;Plugin则是在Webpack运行生命周期中挂钩执行,扩展Webpack功能,如打包优化等。熟悉几个典型Loader/Plugin(如babel-loader将ES6转ES5、file-loader处理图片、HtmlWebpackPlugin自动生成HTML等)。
  • Webpack 热更新 HMR:了解Webpack热模块替换(Hot Module Replacement)的原理。即在开发模式下,Webpack-dev-server建立WebSocket连接,检测到源码变动时,将新的模块代码通过WebSocket推送到浏览器并替换旧模块,而不刷新整个页面。能简单描述HMR的流程和好处(保留应用状态、提高开发效率)。
  • Babel 转译:掌握 Babel 将ES6+代码转换为向后兼容JS的流程。包括:解析(Parsing)–将源码解析成抽象语法树(AST);转换(Transforming)–按照配置的插件预设对AST节点进行变换(比如箭头函数转普通函数等);生成(Generating)–根据转换后的AST重新生成JS代码。这一流程体现前端工程的编译机制,高级面试有可能深入问到。
  • Lint与单测:了解代码质量工具如ESLint规则定制,理解如何封装自定义ESLint插件(知道其遍历AST的基本机制,可简单提及)。熟悉前端单元测试的意义和常用框架(Jest等),虽不一定细问实现,但展示工程化素养有利。
  • 模块化规范:掌握主流JS模块化方案的区别。CommonJS模块用于Node.js,采用同步require()加载模块,加载的是模块的输出对象(值拷贝);ES6 Module标准在编译时确定模块依赖与输出接口,支持import/export,是静态分析加载,导出的是只读引用。因此ES6模块可以进行Tree-shaking优化未使用代码。还可了解AMD、CMD等历史方案及它们为何逐渐被ES6模块取代(AMD异步加载适合浏览器但语法不直观,ES6模块语法更简洁统一)。

以上分类几乎涵盖了前端面试高频知识点。建议在复习中对照清单逐一过一遍,对于不熟悉的概念及时查阅加深理解。

二、需要深入掌握的重点内容(结合偏好与背景)

面试准备时间有限,应有所侧重。根据快手面试偏好和候选人自身背景,以下几个模块和知识值得投入更多精力深度掌握:

  • React 原理及框架比较:作为候选人的强项,React相关原理需深入掌握,以便在面试中展现出比一般候选人更深的理解。快手等大厂常会深入追问框架实现,比如虚拟DOM、Diff算法或让你对比React和Vue的区别。充分利用你对React的实践经验,深入研究其底层机制(如Fiber架构、合成事件等)能让你的回答更有亮点。由于你未使用过Vue,也应提前了解Vue的基本理念以防比较类问题——深度不必过大,但需保证回答时不至于一片空白。

  • 性能优化(尤其是首屏和交互性能):这是你有实际经验的领域,也是快手这样用户量巨大的平台非常看重的方面。面试中很可能被问到如何优化某个性能瓶颈场景,例如如何改善首屏加载过慢的问题或者让某产品在弱网环境下更流畅。你应当深入掌握各层面的性能优化技巧(加载、渲染、编码等)并准备好结合自己项目的案例说明。快手面试中曾直接询问候选人在小程序中做了哪些性能优化,可见对此的重视。充分利用你在性能调优上的实践,整理出一套系统的优化思路,在面试时娓娓道来将非常加分。

  • 浏览器原理与 JS 基础:这些是前端面试的地基,往往决定了面试官对你深度和广度的评价。大厂面试喜欢从基础概念引申出有坑的提问方式(例如用实例考察事件循环而非直接问概念)。鉴于此,你需要对基础知识(事件循环、作用域闭包、原型链、DOM/CSS机制等)烂熟于心并融会贯通。结合你的全栈背景,可以更深入理解一些浏览器机制(如渲染线程与JS线程的关系),这会让你的回答更具说服力。在复习时着重理解原理而非死记结论,这样无论面试怎么变形问,你都能应对自如。

  • 安全知识:你在安全方面有实际开发经验,这是你的优势领域。大厂前端面试经常会问到XSS/CSRF的防范措施等安全问题,而许多应届生在这方面比较薄弱。如果你能够给出丰富且细致的回答(例如说明输出编码、防御CSRF的多种手段并结合后端验证机制等)势必让面试官眼前一亮。由于你有后端开发背景,可以从全栈视角阐述安全方案(例如服务端如何配合防护),体现出全面的安全意识。建议重点熟记常见Web漏洞的成因及防御策略,在面试中适当展现你的安全敏感度。

  • 工程化与项目实践:考虑到你的背景涉及Go、Docker等后端和部署技术,快手面试官可能会对你工程化思维和全栈能力感兴趣。虽然校招前端岗位不一定深入考察后端知识,但前端工程化是常考点,你应深入理解构建工具链原理(Webpack、Babel 等)以应对高难度提问。例如快手有面试过 Babel 转码流程、自定义 ESLint 插件等偏工程化的问题,你可以利用自己的学习和项目经验,在这些问题上给出有深度的回答。同时,你可以准备一些关于如何利用Docker部署前端应用、前后端协作优化等话题的见解,在面试自由提问环节或简历详聊时展现你工程化方面的成熟度。

聚焦上述重点将使你的优势最大化。在这两天里,对以上内容多花时间深挖细节、整理输出框架。同时,对于算法和手写代码类题目可以不做特别准备(题目要求已明确不考),把精力专注在知识版块上。对于Vue框架细节则可浅尝辄止,做到心中有数即可:毕竟两天很难从零精通Vue,不如把时间用于强化React和共通原理,并准备好说明愿意快速学习新框架的态度即可。

三、复习顺序与时间安排(两天规划)

总体思路:先夯实基础再冲刺重点,合理分配时间,以确保广度覆盖的同时突出重点深度。在仅有的两天内,每天划分几个时段,交替复习不同模块以保持效率。具体安排如下:

Day 1 上午:基础巩固(HTML/CSS & JavaScript)
利用第一天清晨头脑最清醒的时段,快速复习前端基础知识。先花约2小时梳理 HTML/CSS 基础:回顾盒模型、布局技巧、常见样式问题等(可通过浏览前端面试题总结快速浏览相关问答)。然后用约2小时强化 JavaScript核心:专注作用域、闭包、原型链、this、事件循环等高频考点,结合代码片段加深理解。如果遇到模糊的概念,及时查MDN文档或笔记搞清原理。这个基础模块复习既是单独考点准备,也是后续理解高级专题(比如框架原理)的前提。

Day 1 下午:框架原理攻坚(React 为主,兼顾 Vue)
下午精力充沛时段,重点突破 React 工作原理。花约1小时整理React生命周期、状态更新机制、合成事件模型等知识点,建议边画结构图边总结(如React Fiber架构示意)。接下来约1小时专攻 Virtual DOM & Diff算法:可以找资料阅读React Diff原理概要并在纸上演练一次虚拟DOM diff过程。然后用约0.5~1小时了解 Vue核心原理:重点弄清响应式数据绑定和虚拟DOM在Vue中的实现,顺带看看Vue的生命周期名称,以防被问及。最后留出约0.5小时对比React和Vue:总结一份两者区别的清单(可以直接对照之前整理的相同/不同点),以便面试回答有条理。下午这段复习完成后,框架类问题你将胸有成竹。

Day 1 晚上:网络与安全
晚上注意力稍下降,适合阅读和记忆类复习。首先用约1小时浏览 计算机网络 相关知识:重点看HTTP协议要点(方法、状态码、缓存机制、HTTP2特性等),可以通过笔记或题库快速QA形式自测。一些需要记忆的地方(如HTTP状态码的意义)可以采取快速背诵+默写要点的方式巩固。接下来用约1小时集中学习 Web安全 模块:建议阅读XSS和CSRF的典型案例和防护方案博客,加深印象,然后合上资料尝试自己复述完整的攻击过程和防御措施。如果时间富余,也可以了解下常见浏览器安全策略(如同源策略、CORS原理)等。Day1晚上结束前,可简单回顾一下当天内容,加深记忆的同时列出尚存疑惑的点,准备第二天解决。

Day 2 上午:性能优化专项
第二天早上精力恢复,投入到 性能优化 模块的复习。用约1小时学习 页面加载优化:包括资源打包优化策略(文件拆分、懒加载)、缓存利用等,可通过阅读性能优化最佳实践文章获取要点,并对照自己项目看用了哪些手段。然后用约1小时复习 渲染性能优化:重点关注如何减少重排重绘、提升动画流畅度等,必要时把浏览器渲染原理再过一遍以理解优化背后的原因。接下来约0.5小时复习 算法优化和内存优化(虽不考算法题,但理解DOM操作耗时、JS大数据处理优化如Web Worker等,可防面试讨论到相关场景)。最后留约0.5小时,整理一套性能优化方案清单:按加载优化、渲染优化、代码优化分类写下关键词和措施。这套清单既是知识点总结,也可在面试问到性能时作为答题提纲瞬时调用。

Day 2 中午/下午前段:工程化与模块化
利用中午或下午早些时候的时间段(约1.5小时),复习 前端工程化 相关内容。首先回顾Webpack的核心概念和常见配置项,着重理解Loader/Plugin机制和构建流程,可结合网上的Webpack面试题整理快速浏览问答。然后花0.5小时看 Babel/Polyfill 相关知识,确保能解释 Babel 转码流程和常用的转译插件。接下来0.5小时熟悉 Git流程和工具:重点看团队协作中的分支管理、多人冲突解决等,理清rebase和merge区别以及语义化提交等概念,以防面试官顺带提及。再用0.5小时回顾 模块化演进:将CommonJS、ES6 Module差异点认真看一遍,记忆关键区别(同步/异步、值拷贝/引用等),想象如果被问到如何回答简练。经过这一中段时间,你对工程化的知识会成体系,不至于在相关问题上语焉不详。

Day 2 下午后段:综合串讲与模拟
在最后半天里,进行综合冲刺和查漏补缺。首先用约1小时时间快速串讲之前所有笔记:可以尝试闭卷回忆每个模块的要点,然后对照清单补充遗忘处。这种主动回忆有助于将知识从短期记忆转为长期记忆,便于第二天面试调用。接着,用约1小时进行模拟问答练习:可以找到一套前端八股文题库,随机抽取问题,快速在脑中组织答案并口述出来。这一步非常重要,它能暴露出哪些知识你虽然看了但讲不出来。对于发现的薄弱点,立即翻看资料再次理解,确保面试时能流畅表述。最后半小时左右,浏览常见面试总结博客或题单,获取一些流行高频题的信息(例如最近流行问AST的可以瞄一眼,但不要深陷新知识),做到心中有数。收尾时调整心态,整理好纸质/脑图版的知识框架,至此你已经最大程度地覆盖了面试知识点。晚上尽量保证休息,为面试保持良好精神状态。

两天的紧凑复习后,你应该对核心知识了然于胸。面试中若遇到没准备到的问题,也不必紧张,尽量根据相关基础知识现场分析应答。通过上述计划的高效复习,你将以更沉稳自信的姿态迎接快手前端面试。

四、参考资料

在复习过程中,可借助一些优质题库和博文来高效获取要点知识和典型问答。推荐参考以下资源:

  • GitHub仓库:wangwenjie1314/webQd – 前端高频面试题合集,按照模块(HTML&CSS、JS、Vue、React、工程化、网络等)整理了2024年最新常考知识要点。该仓库汇总了掘金优秀文章和网友面经,是刷题背题的极佳索引。

  • 稀土掘金「前端八股文」系列 – 作者wakaka378等的专题文章,涵盖浏览器、网络、性能、工程化等各方面八股文要点。例如其中的**《前端铜九铁十面试必备八股文——浏览器》深入总结了XSS/CSRF、防抖节流、事件机制等,《网络相关》**篇则整理了HTTP各版本特点等。这些系列文章语言精炼、要点突出,非常适合考前速览复习。

  • CSDN博客:「2024年Web前端最新面试八股文」 – 综合性前端面试知识大全。该博文按章节详细列出了前端各领域典型题目和参考答案,从CSS、JS基础到框架、性能、安全一应俱全。内容更新至2024年末,涵盖面广且附有解释,适合作为知识盲点查漏和答案表述学习的参考。

  • 知乎专栏:《2024年面试必问的Web前端面试八股文及答案整理》 – 综合整理了一份前端核心考点宝典,涵盖JavaScript、CSS、ES6、Vue2/3、React、Node.js、小程序、HTTP、TypeScript等几乎所有类别。每个知识点都有详解答案,可用于在理解基础上背诵关键表述。这类知乎回答集合凝练了大量面经精华,质量较高。

  • MDN Web Docs(Mozilla开发者文档) – 权威技术细节参考。当遇到概念模糊时,MDN上的相关页面通常提供权威而清晰的解释。例如查看“事件循环(Event Loop)”条目可了解JS运行时模型的准确描述。MDN文档多为英文,但内容可信且细节充分,查阅具体概念(如HTTP缓存、CSS属性等)时非常有价值。

  • 牛客网 NowCoder 前端面试版块 – 聚集了大量实时更新的面试经验帖和真题讨论。可以搜索“快手 前端 面经”获取往届真实问答情境,了解快手面试官爱问的问题类型。利用碎片时间翻阅几篇高赞面经,有助于掌握面试侧重点和应答技巧。不过需注意面经内容良莠不齐,切忌被他人负面经历影响心态。

上述资料以中文为主,信息密度高,十分契合考前快速复习的需求。建议合理利用这些资源:提纲挈领学习知识,并参考优秀答案的表述来优化自己的回答方式。通过题库演练与文档精读相结合,你将对八股文考点理解更深、记忆更牢,在面试中游刃有余,争取一举拿下快手前端校招的Offer!

show all >>

42

  2025-03-27
字数统计: 1k字   |   阅读时长: 5min

QUESTION:

42.md

My Think:

滚瓜烂熟的一道题, 全部靠背诵. 但是昨天又理解了一下, 画了一个GIF图供我自己理解, 代码如下,

图片举例: [0,1,0,2,1,0,1,3,2,1,2,1]
该题的核心思想其实就是木桶原理, 我们将最外面的两个柱子视作玛丽亚之墙, 这个时候我们忽略玛丽亚之墙内部的城墙, 那么最多是不是可以装min(leftmax, rightmax)这么高的水呢?
也就是最短的柱子的水决定了它的”高度”. 但是我们想知道最多能装多少水, 还需要一个宽度. 于是这个时候我们就一根一根柱子看, 也就是说我们计算每”1”个宽度能装多少水, 然后加起来即可.
我们的左指针为left, 右指针为right, 我们再维护一个左边最高和右边最高.
当我们到达最外面的玛丽亚之墙的时候, 左最高即为leftmax, 右最高即为rightmax, 这个时候我们是无法判断可以装多少水的.
一直到leftmost!=rightmost的时候, 我们就可以判断了.
img_6.png
比如这一帧, 我们最多能装的是leftmax的水量, 即2, 但是因为我们这个时候左指针所在的柱子有一定高度1, 所以是2-1=1, 也就是我们能装1的水.
如果我们比较的不是leftmost和rightmost, 而是leftmax和rightmax, 我们就无法确定说这一个柱子为什么能够兜得住水, 它之所以能兜住水,必然是<=leftmax的

This is a problem I’ve memorized inside out — totally muscle memory. But I revisited it yesterday and tried to actually understand it. I even made a GIF to help myself visualize what’s happening. Here’s the code:

Let’s use the example: [0,1,0,2,1,0,1,3,2,1,2,1]

The core idea of this problem is basically the “bucket theory”:
We treat the two outermost bars as the “Walls of Maria” — and ignore the inner ones for now.
If that’s the case, then the max height of water we can hold is min(leftmax, rightmax).
In other words, the shorter wall decides the water level.

But height alone isn’t enough — we also need width to compute the actual amount of water.
So we look at each bar one by one, and calculate how much water we can trap on top of it, then sum it all up.

We use two pointers: left and right, and also keep track of the highest wall on the left (leftmax) and the highest wall on the right (rightmax).

When our pointer reaches the end of the string (or the two walls meet), that means we’ve gone through all the characters — that’s when we know we have a valid answer.

Take this specific frame as an example:
img_6.png

At this point, the max water we can hold is leftmax = 2, but the current column has height 1, so we can trap:

2 - 1 = 1 unit of water.

If we were comparing leftmax and rightmax directly, we wouldn’t know why this particular column can hold water. The only reason it can trap water is because its height is less than or equal to leftmax.

Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution:
def trap(self, height: list[int]) -> int:
ans = leftmost = rightmost = 0
left, right = 0, len(height) - 1
while left < right:
leftmost = max(leftmost, height[left])
rightmost = max(rightmost, height[right])
if leftmost <= rightmost:
ans += leftmost - height[left]
left += 1
else:
ans += rightmost - height[right]
right -= 1
return ans
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np

# Given height list for illustration
height = [0,1,0,2,1,0,1,3,2,1,2,1]

# Initialize variables as in the function
left, right = 0, len(height) - 1
leftmax = rightmax = 0
ans = 0

# For animation, store each frame's water level and pointers
frames = []

# Simulate the logic and capture frames
while left < right:
leftmax = max(leftmax, height[left])
rightmax = max(rightmax, height[right])
water = [0] * len(height)
if leftmax <= rightmax:
trapped = max(0, leftmax - height[left])
ans += trapped
water[left] = trapped
frames.append((height.copy(), water.copy(), left, right))
left += 1
else:
trapped = max(0, rightmax - height[right])
ans += trapped
water[right] = trapped
frames.append((height.copy(), water.copy(), left, right))
right -= 1

# Create animation
fig, ax = plt.subplots(figsize=(10, 5))

def update(frame):
ax.clear()
heights, water, l_ptr, r_ptr = frame
indices = np.arange(len(heights))
ax.bar(indices, heights, color='grey', edgecolor='black')
ax.bar(indices, water, bottom=heights, color='blue', edgecolor='blue', alpha=0.6)
ax.axvline(l_ptr, color='green', linestyle='--', label='Left Pointer')
ax.axvline(r_ptr, color='red', linestyle='--', label='Right Pointer')
ax.set_ylim(0, max(height) + 3)
ax.set_title("Trapping Rain Water Animation")
ax.legend()

ani = animation.FuncAnimation(fig, update, frames=frames, interval=500, repeat=False)

from IPython.display import HTML
ani.save("trapping_rain_water.gif", writer="pillow", fps=2) # 保存为 GIF
  • Python
  • Answer

show all >>

46.permutations

  2025-03-27
字数统计: 362字   |   阅读时长: 2min

Description:

Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order.

Example 1:

Input: nums = [1,2,3]
Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
Example 2:

Input: nums = [0,1]
Output: [[0,1],[1,0]]
Example 3:

Input: nums = [1]
Output: [[1]]

Thinking:

This question is more like a tree problem.
like the tree below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
dfs(0): nums = [1,2,3]
|
|-- i=0: swap(0,0) -> [1,2,3]
| |
| |-- dfs(1)
| |-- i=1: swap(1,1) -> [1,2,3]
| | |-- dfs(2): append [1,2,3]
| |-- i=2: swap(1,2) -> [1,3,2]
| |-- dfs(2): append [1,3,2]
|
|-- i=1: swap(0,1) -> [2,1,3]
| |
| |-- dfs(1)
| |-- i=1: swap(1,1) -> [2,1,3]
| | |-- dfs(2): append [2,1,3]
| |-- i=2: swap(1,2) -> [2,3,1]
| |-- dfs(2): append [2,3,1]
|
|-- i=2: swap(0,2) -> [3,2,1]
|
|-- dfs(1)
|-- i=1: swap(1,1) -> [3,2,1]
| |-- dfs(2): append [3,2,1]
|-- i=2: swap(1,2) -> [3,1,2]
|-- dfs(2): append [3,1,2]

We swap the current position index with each possible candidate i from index to the end. They can be seen as left and right pointers: index determines which position we’re filling, and i tries different numbers to place there.

Before the recursive call, we swap nums[i] and nums[index] to try placing a new number at position index. If we reach the last position (index == len(nums) - 1), we add the current permutation to the answer list.
After recursion, we swap back to restore the original state (backtracking).

Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
# index
def dfs(index):
# Reach the last element
if index == len(nums) - 1:
res.append(list(nums))
return
for i in range(index, len(nums)):
nums[i], nums[index] = nums[index], nums[i]
dfs(index + 1)
nums[i], nums[index] = nums[index], nums[i]

res = []
dfs(0)
return res
  • Python
  • 9021
  • tree

show all >>

93. Restore IP Addresses

  2025-03-25
字数统计: 500字   |   阅读时长: 2min

QUESTION:

93. Restore IP Addresses

My Think:

This MiHoYo coding test question is very similar to LeetCode 46 (Permutations), and both rely on the backtracking approach.

As shown in the code, during the first traversal, we may get something like ['2', '5', '5', '2'] as our initial parts, but at this point, we haven’t traversed the entire string yet.

When we enter the next level of DFS, the pointer moves forward by +length, so it effectively moves to the far right of the current segment.
If the pointer has reached the end of the string, it means we’ve visited all characters — in this case, we’ve found one valid answer and can add it to the result list.

One important note: parts + [part] is pass-by-value, not by reference like in LeetCode 46.
This means we don’t need to manually undo changes (i.e., no need to backtrack with pop()), because each recursive call creates a new list.

现在在做的是MiHoyo的笔试题, 这个和46.permutation非常相似, 都是回溯思想.

如代码所示, 在第一遍遍历中, 我们会得到['2','5','5','2'] 作为第一个parts, 但是我们并没有遍历完整个字符串.

这个时候如果我们进入新的dfs的时候, 我们的指针因为刚刚+length, 所以我们实际上
指到的是最右边,所以当当前指针已经指到最右边的时候, 说明我们遍历完了所有字符, 那么这就是答案之一.

parts + [part] 是值传递而不是46的引用传递, 所以不用手动撤销更改

Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from typing import List

class Solution:
def restoreIpAddresses(self, s: str) -> List[str]:
res = []

def backtrack(start: int, parts: List[str]):
# 终止条件:正好4段且用完所有字符
# Stop condition: exactly 4 segments and all characters used up
if len(parts) == 4:
if start == len(s):
res.append(".".join(parts))
return

for length in range(1, 4): # 每段长度1~3 Each segment length 1~3
if start + length > len(s):
break
part = s[start:start+length]

# 前导0非法,但0本身合法
# Leading 0 is illegal, but 0 itself is legal
if len(part) > 1 and part[0] == '0':
continue

if int(part) <= 255:
backtrack(start + length, parts + [part]) # 注意用 + 避免污染 We need to use + to avoid pollution

backtrack(0, [])
return res
  • Python
  • Answer

show all >>

9021_TUT_3_25T1

  2025-03-25
字数统计: 2.2k字   |   阅读时长: 13min

Exercise 1

Problem Description:

You are given two integers, m and n, where:

  • m represents the number of repeated units (patterns).
  • n represents the number of elements (underscores) within each unit.

The goal is to generate a string where:

  • Each unit contains n underscores (_), separated by |.
  • These units are then concatenated together, separated by *.

My Solution:

1
2
3
4
5
def generate_struct(n):
return '|'.join(n * ['_'])

def f1(m, n):
return ' * '.join(m * [generate_struct(n)])

My Thought Process:

I think of each unit as a separate structure. So, I decompose the problem by breaking down the smallest unit, which is the structure made of underscores joined by |.
After constructing each unit, I use join() to combine them together using *.

Helper Function generate_struct(n):

This function generates the basic structure.
It creates a list of underscores (_) of length n and joins them with |.
Example: If n = 2, the result will be “_|_“.

Standard Solution:

1
2
def f1(m, n):
return ' * '.join('|'.join('_' for _ in range(n)) for _ in range(m))

Concise Expression:

The inner join creates a string of n underscores joined by | using a generator expression ('_' for _ in range(n)).
The outer join repeats this process m times and concatenates the units using *.

In summary, my solution focuses on modularity by breaking down the problem into smaller parts (like creating a structural unit), whereas the standard solution compresses everything into one line using list comprehensions.

Exercise 2

Problem Description:

The goal is to generate a pattern based on the digits of a given number n. Specifically:

If a digit is odd, it should be represented by a black square (⬛).
If a digit is even, it should be represented by a white square (⬜).
The program takes a number n as input and prints a string of squares corresponding to the digits of n.

My Solution:

1
2
3
4
5
6
7
8
def f2(n):
ans = ''
for i in str(n):
if int(i) % 2 == 0:
ans += '⬜'
else:
ans += '⬛'
print(ans)

My Thought Process:

  1. Loop through each digit:
    Convert the number n to a string to iterate over each digit individually.

  2. Check if the digit is even or odd:
    Convert each digit back to an integer and use the modulus operator (% 2) to check if the digit is even or odd.

  3. Append the corresponding square:

    • If the digit is even, append a white square (⬜) to the result string.
    • If the digit is odd, append a black square (⬛).
  4. Print the final string:
    After processing all the digits, print the final string containing black and white squares.

Standard Solution:

1
2
def f2(n):
print(''.join({0: '\u2b1c', 1: '\u2b1b'}[int(d) % 2] for d in str(n)))

Dr.Martin’s solution is:

  1. More compact and Pythonic.
  2. Uses a dictionary and list comprehension for brevity and efficiency.
  3. The Unicode characters for the squares are referenced directly using their escape sequences (\u2b1c for white, \u2b1b for black).

Exercise 3

Problem Description:

In this task, the number n is treated as a number expressed in different bases (ranging from 2 to 10), and we aim to convert it into its corresponding base 10 value for each of these bases, where the conversion is valid.

For n = 2143:

  • 2143 in base 5 is equivalent to 298 in base 10.
  • 2143 in base 6 is equivalent to 495 in base 10.
  • And so on.

The goal is to iterate over different base systems from 2 to 10, treat the input number n as if it is expressed in each base, and then convert it to base 10.

My Solution:

1
2
3
4
5
6
7
8
def f3(n: int):
for i in range(2, 11):
try:
# Treat n as a number in base i and convert it to base 10
value = int(str(n), i)
print(f'{n} is {value} in base {i}')
except ValueError:
pass

My Thought Process:

  1. Iterating over Bases (2 to 10):

    • We loop through the base values i ranging from 2 to 10.
  2. Conversion Using int():

    • For each base i, we treat the number n as a number in that base. To do this, we first convert n to a string (str(n)) and then use int() to interpret it as a base i number.
    • If the digits of n are valid for base i, this conversion succeeds, and the result is the base 10 equivalent of n.
    • If the digits of n are not valid for base i (for example, if base 2 is used and n contains digits greater than 1), a ValueError is raised, and we skip the invalid base.
  3. Handling Errors with try-except:

    • The try-except block ensures that invalid bases are skipped, allowing us to handle cases where the digits in n are not valid for a particular base.

Standard Solution:

1
2
3
4
5
def f3(n):
n_as_string = str(n)
min_base = max(2, max({int(d) for d in n_as_string}) + 1)
for b in range(min_base, 11):
print(f'{n} is {int(n_as_string, b)} in base {b}')

It skips the bases where the digits in n are not valid, and it uses a set comprehension to extract the unique digits from n_as_string. The maximum digit is then used to determine the minimum base to start iterating from.

Exercise 4

Problem Description:

The task is to create a function f4(n, base) that returns a dictionary D, where:

Keys are integers from 0 to n.
Values are tuples that represent the base base representation of each key, converted from base 10.

My Solution:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def convert_to_base(n, base):
if n == 0:
return '0'
digits = []
while n:
digits.append(str(n % base))
n //= base
return ''.join(digits[::-1])

def f4(n: int, base: int):
D = {}
for i in range(0, n + 1):
D[i] = tuple(map(int, convert_to_base(i, base)))
return D

My Thought Process:

  1. Helper Function convert_to_base(n, base):

    • This function converts a number n from base 10 to the specified base.
    • We use a while loop to repeatedly take the modulus (n % base) and append the remainder to the list digits.
    • We then divide n by base (n //= base) to reduce it for the next iteration.
    • After collecting all digits, we reverse the list and return it as a string.
  2. Main Function f4(n, base):
    We initialize an empty dictionary D.
    For each number i from 0 to n, we convert i to the given base using convert_to_base().
    The converted base digits are then mapped to integers and stored in a tuple as the value for each key i in the dictionary.

Explanation of Why map() is not Pythonic:

In the function f4, the use of map(int, convert_to_base(i, base)) applies the int function
to each element of the result from convert_to_base, effectively converting each character to an integer.

However, it’s worth noting that the map() function, which originates from functional programming,
has largely been superseded by more Pythonic constructs such as list comprehensions.

These comprehensions are generally considered superior for several reasons:

  • They are more elegant and concise.
  • They tend to be shorter in terms of syntax, making the code easier to read.
  • They are easier to understand for most people, especially those who are more familiar with Python’s
    standard syntax rather than functional programming constructs.
  • In many cases, they are also more efficient in terms of performance.

For example, instead of using map(int, ...), the same functionality could be achieved with a
list comprehension, like so:

D[i] = tuple([int(digit) for digit in convert_to_base(i, base)])

This list comprehension achieves the same result but follows a more modern Pythonic style.

Standard Solution:

1
2
3
4
5
6
7
8
9
10
def f4(n, base):
D = {0: (0,)}
for m in range(1, n + 1):
digits = []
p = m
while p:
digits.append(p % base)
p //= base
D[m] = tuple(reversed(digits))
return D

Both solutions are valid and achieve the same result. My approach uses a helper function for base conversion, which adds modularity,
whereas the standard solution is more concise and directly integrates the conversion logic into the main function.

Exercise 5

At the first, try to run this:

1
print(0.1 + 0.2)

What happened? The result is not 0.3, but 0.30000000000000004. Why?

Problem Description:

The approach we are using in this exercise is designed to expose the limitations of floating-point arithmetic in computers. Let’s break down why this approach leads to precision inaccuracies and why other methods might not reveal these issues as clearly.

This problem can seem complex, and it’s designed to highlight the subtleties of floating-point arithmetic. Let’s walk through the logic using the test cases to figure out what the function does.

Key Concepts:

  • Floating-point numbers: Computers store floating-point numbers using a binary format, which often introduces precision errors.
  • Precision: We’re working with two types of precision in this function—simple precision (same as the length of the fractional part) and double precision (twice that length).

Solution:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
def f5(integral_part, fractional_part):
# Calculate the number of digits in the fractional part
precision = len(str(fractional_part))

# Concatenate the integral and fractional parts as strings, then convert to a float
a_float = float(str(integral_part) + '.' + str(fractional_part))

# Format the float with precision equal to the number of digits in the fractional part (simple precision)
simple_precision = f'{a_float:.{precision}f}'

# Append a number of zeros equal to the fractional part length to the simple precision value (extended precision)
extended_simple_precision = simple_precision + '0' * precision

# Format the float with precision equal to twice the number of fractional digits (double precision)
double_precision = f'{a_float:.{precision * 2}f}'

# Print the first part of the output
print('With', precision * 2, 'digits after the decimal point, ', end='')

# Compare if extended precision and double precision values are the same
if extended_simple_precision == double_precision:
# If they are the same, it means the float is precise and has trailing zeros
print(simple_precision, 'prints out with', precision, 'trailing',
precision == 1 and 'zero,' or 'zeroes,', 'namely, as',
extended_simple_precision
)
else:
# If not, there is a precision error, and no trailing zeros
print(simple_precision, 'prints out as', double_precision)

Our function attempts to check and display this floating point error with simple precision (simple_precision) and double precision (double_precision). The error becomes more obvious when we represent floating point numbers with higher precision (double the number of decimal places). So in this way, we show that floating point numbers are not always actually stored as we expect them to be with more precise representation.

Exercise 6

Problem Description:

In this task, we are given:

  • A list L, which contains multiple sub-lists of integers, and all sub-lists have the same length n.
  • A list fields, which is a permutation of {1, ..., n}.

We are required to sort the list L by using a multi-key sorting mechanism, where:

  • First, the elements in L are sorted based on the position given by the first element of fields.
  • If two sub-lists are equal based on the first field, we move to the second field, and so on.
  • Finally, if required, the sorting proceeds up to the last field in fields.

Example of Fields Explanation:

If fields = [2, 1], it means:

  1. First, sort based on the second element of each sublist.
  2. If there are ties (same values in the second position), sort based on the first element.

My Solution:

1
2
def f6(L, fields):
return sorted(L, key=lambda x: [x[i-1] for i in fields])
  1. Sorting with sorted():
    The sorted() function is used to sort the list L.
    The key parameter defines how the sorting will be performed.

  2. Lambda Function:
    The lambda function defines how the sublists will be sorted. It generates a list of values for each sublist based on the positions specified in fields.
    For example, if fields = [2, 1], the lambda function will extract the second and first elements from each sublist in that order, and sorting will be done based on this new list.

  3. Key Structure:
    The key is a list of elements from each sublist, indexed by the positions specified in fields. We use x[i - 1] because fields is 1-based, and list indexing in Python is 0-based.

  4. What is Lambda Function?

For example:

We have:

1
f = lambda x: x * x

This is equivalent to:

1
2
def f(x):
return x * x

And lambda function in a sorted function is used to define a custom sorting key.

1
2
3
4
5
L = [(1,2), (3,1), (5,0)]

SortedL = sorted(L, key=lambda x: x[1])

print(SortedL)

The result is: [(5, 0), (3, 1), (1, 2)], it sorts the list based on the second element of each tuple.

Standard Solution:

1
2
def f6(L, fields):
return sorted(L, key=lambda x: tuple(x[i - 1] for i in fields))

Why Use a Tuple?:

  • Tuples are often preferred for multi-key sorting because they are immutable, and Python’s built-in sorting functions can efficiently compare tuples.
  • Each sublist is transformed into a tuple of its elements based on the order defined by fields. The sorted() function then uses these tuples to sort the list.
  • 9021
  • lab

show all >>

9021_TUT_1_25T1

  2025-03-25
字数统计: 952字   |   阅读时长: 5min

Introduction How Labs work

We can see the structure of the lab is like this:

1
2
3
%%run_and_test python3 -c "from exercise_1_1 import f1; print(f1(0, 0))"

'\n'

Which means run_and_test ? It is a Magic Command, run python file which is exercise_1_1.py and test the output of f1(0, 0).

And at the top of this Jupyter notebook, we can see the following code:

1
load_ext run_and_test

This is a magic command that loads the run_and_test extension, which allows us to run and test Python code in the notebook.

And if you run %pycat exercise_1_1_template.py, you will get a new file exercise_1_1.py which is the template of the exercise.

You can write your code inside the exercise_1_1.py file and run the test code in the notebook to check if your code is correct. Or directly write your code after %%writefile exercise_1_1.py command block.

Exercise 1

Finding the pattern, we can see that the first parameter m means how many structures there are, and the second parameter n means how many underscores there are.

So we can assume the structure is like:

1
|____|

And if we have m = 3 and n = 4, the output would be:

1
|____||____||____|

My solution

In Python, we can use multiplication to repeat a string. So we can use the following code to solve this problem:

1
2
def f1(m, n):
return ('|' + '_' * n + '|') * m

Exercise 2

Same way to find the pattern, n rows and n columns, and each row contains the digit n repeated n times.

And you can find, the input n is a digit, which means it is an integer. While we use “*” with the integer, it is the mathematical multiplication.

So we need to convert the integer to a string, and then multiply it with the integer, and add a newline at the end of each row.

My solution

1
2
def f2(n):
return (str(n)*n + "\n")*n

And… in case you want to use a for loop:

1
2
3
4
5
def f2(n):
result = ""
for i in range(n):
result += str(n) * n + "\n"
return result

Exercise 3

This problem is a bit complex to understand, so let’s break it down step by step. Based on Eric’s reply in Ed, we can see that the problem requires us to traverse from the rightmost side, removing all elements that are ≤ the last element (x) until we encounter a number that is ≥ the last element (y). It is important to note that if there are any elements to the left of y that were previously removed, they should also be removed.

This Exercise needs us use only 1 loop.

My solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def f3(L):
# You can use [] list here, but set is more effective
num_remove = set()
# Check ED of Eric's reply
i = len(L)-2
flag = 0
while i >= 0:
if L[i] < L[-1] and flag == 0:
num_remove.add(L[i])
L.pop(i)
# reset the length in case it have nothing
i = len(L) - 2
elif L[i] >= L[-1]:
i -= 1
flag = 1
elif L[i] in num_remove:
L.pop(i)
else:
i -= 1
print(L)
You can full screen, change speed, and change quality of the video.

Standard Solution

I have to say Eric is a genius:

1
2
3
4
def f3(L):
while len(L) > 1 and L[-2] < L[-1]:
L.remove(L[-2])
print(L)
You can full screen, change speed, and change quality of the video.

Exercise 4

The fourth function, f4, works with dictionaries:

1
2
3
4
5
6
7
8
def f4(D, n):
if n not in D:
return []
ans = [n]
while n in D and D[n] > n:
ans.append(D[n])
n = D[n]
return ans
  1. This function takes a dictionary D and an integer n. It generates a strictly increasing sequence of integers based on the relationships defined in the dictionary.
  2. Starting from n, it appends D[n], D[D[n]], and so on to the list, as long as each subsequent value is greater than the previous one.
  3. For example, if D = {1: 2, 2: 3, 3: 5} and n = 1, the function will return [1, 2, 3, 5].

Standard Solution

1
2
3
4
5
6
def f4(D, n):
L = [] if n not in D else [n]
while n in D and n < D[n]:
L.append(D[n])
n = D[n]
return L

Exercise 5

The fifth function, f5, reads from a file and processes each line:

1
2
3
4
5
6
def f5(filename):
with open(filename) as f:
for i in f:
name, number = i.split(',')
number = int(number) * 1000
print(f"{number} people named {name}")
  1. This function reads a text file, where each line consists of a name and a count separated by a comma.
  2. The function multiplies the count by 1000 and prints the result in the format: “X people named Y”.
  3. For example, if the file contains a line “John,5”, it will print:
1
5000 people named John

Standard Solution

1
2
3
4
5
def f5(filename):
with open(filename) as file:
for line in file:
name, count = line.split(',')
print(int(count) * 1_000, 'people named', name)

Exercise 6

The final function, f6, also reads from a text file, but with a different format:

1
2
3
4
5
6
7
def f6(filename):
with open(filename) as f:
for i in f:
# Check if the line is not empty
if not i.isspace():
number, charactor = i.split()
print(int(number) * charactor)
  1. This function reads lines that contain an integer followed by a symbol (e.g., “5 *”).
  2. It multiplies the symbol by the integer and prints the result.
  3. For example, if the file contains the line “3 # “, the function will print:
1
### 
  • “9021”
  • Tutorial
  • Lab

show all >>

9021_TUT_3_25T1

  2025-03-07
字数统计: 2.2k字   |   阅读时长: 13min

Exercise 1

Problem Description:

You are given two integers, m and n, where:

  • m represents the number of repeated units (patterns).
  • n represents the number of elements (underscores) within each unit.

The goal is to generate a string where:

  • Each unit contains n underscores (_), separated by |.
  • These units are then concatenated together, separated by *.

My Solution:

1
2
3
4
5
def generate_struct(n):
return '|'.join(n * ['_'])

def f1(m, n):
return ' * '.join(m * [generate_struct(n)])

My Thought Process:

I think of each unit as a separate structure. So, I decompose the problem by breaking down the smallest unit, which is the structure made of underscores joined by |.
After constructing each unit, I use join() to combine them together using *.

Helper Function generate_struct(n):

This function generates the basic structure.
It creates a list of underscores (_) of length n and joins them with |.
Example: If n = 2, the result will be “_|_“.

Standard Solution:

1
2
def f1(m, n):
return ' * '.join('|'.join('_' for _ in range(n)) for _ in range(m))

Concise Expression:

The inner join creates a string of n underscores joined by | using a generator expression ('_' for _ in range(n)).
The outer join repeats this process m times and concatenates the units using *.

In summary, my solution focuses on modularity by breaking down the problem into smaller parts (like creating a structural unit), whereas the standard solution compresses everything into one line using list comprehensions.

Exercise 2

Problem Description:

The goal is to generate a pattern based on the digits of a given number n. Specifically:

If a digit is odd, it should be represented by a black square (⬛).
If a digit is even, it should be represented by a white square (⬜).
The program takes a number n as input and prints a string of squares corresponding to the digits of n.

My Solution:

1
2
3
4
5
6
7
8
def f2(n):
ans = ''
for i in str(n):
if int(i) % 2 == 0:
ans += '⬜'
else:
ans += '⬛'
print(ans)

My Thought Process:

  1. Loop through each digit:
    Convert the number n to a string to iterate over each digit individually.

  2. Check if the digit is even or odd:
    Convert each digit back to an integer and use the modulus operator (% 2) to check if the digit is even or odd.

  3. Append the corresponding square:

    • If the digit is even, append a white square (⬜) to the result string.
    • If the digit is odd, append a black square (⬛).
  4. Print the final string:
    After processing all the digits, print the final string containing black and white squares.

Standard Solution:

1
2
def f2(n):
print(''.join({0: '\u2b1c', 1: '\u2b1b'}[int(d) % 2] for d in str(n)))

Dr.Martin’s solution is:

  1. More compact and Pythonic.
  2. Uses a dictionary and list comprehension for brevity and efficiency.
  3. The Unicode characters for the squares are referenced directly using their escape sequences (\u2b1c for white, \u2b1b for black).

Exercise 3

Problem Description:

In this task, the number n is treated as a number expressed in different bases (ranging from 2 to 10), and we aim to convert it into its corresponding base 10 value for each of these bases, where the conversion is valid.

For n = 2143:

  • 2143 in base 5 is equivalent to 298 in base 10.
  • 2143 in base 6 is equivalent to 495 in base 10.
  • And so on.

The goal is to iterate over different base systems from 2 to 10, treat the input number n as if it is expressed in each base, and then convert it to base 10.

My Solution:

1
2
3
4
5
6
7
8
def f3(n: int):
for i in range(2, 11):
try:
# Treat n as a number in base i and convert it to base 10
value = int(str(n), i)
print(f'{n} is {value} in base {i}')
except ValueError:
pass

My Thought Process:

  1. Iterating over Bases (2 to 10):

    • We loop through the base values i ranging from 2 to 10.
  2. Conversion Using int():

    • For each base i, we treat the number n as a number in that base. To do this, we first convert n to a string (str(n)) and then use int() to interpret it as a base i number.
    • If the digits of n are valid for base i, this conversion succeeds, and the result is the base 10 equivalent of n.
    • If the digits of n are not valid for base i (for example, if base 2 is used and n contains digits greater than 1), a ValueError is raised, and we skip the invalid base.
  3. Handling Errors with try-except:

    • The try-except block ensures that invalid bases are skipped, allowing us to handle cases where the digits in n are not valid for a particular base.

Standard Solution:

1
2
3
4
5
def f3(n):
n_as_string = str(n)
min_base = max(2, max({int(d) for d in n_as_string}) + 1)
for b in range(min_base, 11):
print(f'{n} is {int(n_as_string, b)} in base {b}')

It skips the bases where the digits in n are not valid, and it uses a set comprehension to extract the unique digits from n_as_string. The maximum digit is then used to determine the minimum base to start iterating from.

Exercise 4

Problem Description:

The task is to create a function f4(n, base) that returns a dictionary D, where:

Keys are integers from 0 to n.
Values are tuples that represent the base base representation of each key, converted from base 10.

My Solution:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def convert_to_base(n, base):
if n == 0:
return '0'
digits = []
while n:
digits.append(str(n % base))
n //= base
return ''.join(digits[::-1])

def f4(n: int, base: int):
D = {}
for i in range(0, n + 1):
D[i] = tuple(map(int, convert_to_base(i, base)))
return D

My Thought Process:

  1. Helper Function convert_to_base(n, base):

    • This function converts a number n from base 10 to the specified base.
    • We use a while loop to repeatedly take the modulus (n % base) and append the remainder to the list digits.
    • We then divide n by base (n //= base) to reduce it for the next iteration.
    • After collecting all digits, we reverse the list and return it as a string.
  2. Main Function f4(n, base):
    We initialize an empty dictionary D.
    For each number i from 0 to n, we convert i to the given base using convert_to_base().
    The converted base digits are then mapped to integers and stored in a tuple as the value for each key i in the dictionary.

Explanation of Why map() is not Pythonic:

In the function f4, the use of map(int, convert_to_base(i, base)) applies the int function
to each element of the result from convert_to_base, effectively converting each character to an integer.

However, it’s worth noting that the map() function, which originates from functional programming,
has largely been superseded by more Pythonic constructs such as list comprehensions.

These comprehensions are generally considered superior for several reasons:

  • They are more elegant and concise.
  • They tend to be shorter in terms of syntax, making the code easier to read.
  • They are easier to understand for most people, especially those who are more familiar with Python’s
    standard syntax rather than functional programming constructs.
  • In many cases, they are also more efficient in terms of performance.

For example, instead of using map(int, ...), the same functionality could be achieved with a
list comprehension, like so:

D[i] = tuple([int(digit) for digit in convert_to_base(i, base)])

This list comprehension achieves the same result but follows a more modern Pythonic style.

Standard Solution:

1
2
3
4
5
6
7
8
9
10
def f4(n, base):
D = {0: (0,)}
for m in range(1, n + 1):
digits = []
p = m
while p:
digits.append(p % base)
p //= base
D[m] = tuple(reversed(digits))
return D

Both solutions are valid and achieve the same result. My approach uses a helper function for base conversion, which adds modularity,
whereas the standard solution is more concise and directly integrates the conversion logic into the main function.

Exercise 5

At the first, try to run this:

1
print(0.1 + 0.2)

What happened? The result is not 0.3, but 0.30000000000000004. Why?

Problem Description:

The approach we are using in this exercise is designed to expose the limitations of floating-point arithmetic in computers. Let’s break down why this approach leads to precision inaccuracies and why other methods might not reveal these issues as clearly.

This problem can seem complex, and it’s designed to highlight the subtleties of floating-point arithmetic. Let’s walk through the logic using the test cases to figure out what the function does.

Key Concepts:

  • Floating-point numbers: Computers store floating-point numbers using a binary format, which often introduces precision errors.
  • Precision: We’re working with two types of precision in this function—simple precision (same as the length of the fractional part) and double precision (twice that length).

Solution:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
def f5(integral_part, fractional_part):
# Calculate the number of digits in the fractional part
precision = len(str(fractional_part))

# Concatenate the integral and fractional parts as strings, then convert to a float
a_float = float(str(integral_part) + '.' + str(fractional_part))

# Format the float with precision equal to the number of digits in the fractional part (simple precision)
simple_precision = f'{a_float:.{precision}f}'

# Append a number of zeros equal to the fractional part length to the simple precision value (extended precision)
extended_simple_precision = simple_precision + '0' * precision

# Format the float with precision equal to twice the number of fractional digits (double precision)
double_precision = f'{a_float:.{precision * 2}f}'

# Print the first part of the output
print('With', precision * 2, 'digits after the decimal point, ', end='')

# Compare if extended precision and double precision values are the same
if extended_simple_precision == double_precision:
# If they are the same, it means the float is precise and has trailing zeros
print(simple_precision, 'prints out with', precision, 'trailing',
precision == 1 and 'zero,' or 'zeroes,', 'namely, as',
extended_simple_precision
)
else:
# If not, there is a precision error, and no trailing zeros
print(simple_precision, 'prints out as', double_precision)

Our function attempts to check and display this floating point error with simple precision (simple_precision) and double precision (double_precision). The error becomes more obvious when we represent floating point numbers with higher precision (double the number of decimal places). So in this way, we show that floating point numbers are not always actually stored as we expect them to be with more precise representation.

Exercise 6

Problem Description:

In this task, we are given:

  • A list L, which contains multiple sub-lists of integers, and all sub-lists have the same length n.
  • A list fields, which is a permutation of {1, ..., n}.

We are required to sort the list L by using a multi-key sorting mechanism, where:

  • First, the elements in L are sorted based on the position given by the first element of fields.
  • If two sub-lists are equal based on the first field, we move to the second field, and so on.
  • Finally, if required, the sorting proceeds up to the last field in fields.

Example of Fields Explanation:

If fields = [2, 1], it means:

  1. First, sort based on the second element of each sublist.
  2. If there are ties (same values in the second position), sort based on the first element.

My Solution:

1
2
def f6(L, fields):
return sorted(L, key=lambda x: [x[i-1] for i in fields])
  1. Sorting with sorted():
    The sorted() function is used to sort the list L.
    The key parameter defines how the sorting will be performed.

  2. Lambda Function:
    The lambda function defines how the sublists will be sorted. It generates a list of values for each sublist based on the positions specified in fields.
    For example, if fields = [2, 1], the lambda function will extract the second and first elements from each sublist in that order, and sorting will be done based on this new list.

  3. Key Structure:
    The key is a list of elements from each sublist, indexed by the positions specified in fields. We use x[i - 1] because fields is 1-based, and list indexing in Python is 0-based.

  4. What is Lambda Function?

For example:

We have:

1
f = lambda x: x * x

This is equivalent to:

1
2
def f(x):
return x * x

And lambda function in a sorted function is used to define a custom sorting key.

1
2
3
4
5
L = [(1,2), (3,1), (5,0)]

SortedL = sorted(L, key=lambda x: x[1])

print(SortedL)

The result is: [(5, 0), (3, 1), (1, 2)], it sorts the list based on the second element of each tuple.

Standard Solution:

1
2
def f6(L, fields):
return sorted(L, key=lambda x: tuple(x[i - 1] for i in fields))

Why Use a Tuple?:

  • Tuples are often preferred for multi-key sorting because they are immutable, and Python’s built-in sorting functions can efficiently compare tuples.
  • Each sublist is transformed into a tuple of its elements based on the order defined by fields. The sorted() function then uses these tuples to sort the list.
  • 9021
  • lab

show all >>

9021_TUT_7

  2025-03-02
字数统计: 1.4k字   |   阅读时长: 8min

Exercise 1

what is yield in python?

The yield keyword in Python is used to create generator functions. A generator function is a special kind of function that returns a generator iterator, which can be used to iterate over a sequence of values. Unlike a regular function that returns a single value using the return statement, a generator function can yield multiple values, one at a time, pausing its state between each one.

How it works?

When a generator function is called, it doesn’t execute its code immediately. Instead, it returns a generator object that can be iterated over. Each time you request the next item from the generator (using next() or a loop), the generator function resumes execution from where it last left off, runs until it hits a yield statement, and yields the value to the caller.

Problem Description

First, we need to understand the requirements of the problem:

Input: A list of integers L, such as [n1, n2, n3, …]. Output:

  • Output n1 lines of rank 1 X, which means X without indentation.
  • Between each pair of rank 1 X, output n2 lines of rank 2 X, which are indented with one tab (\t).
  • Between each pair of rank 2 X, output n3 lines of rank 3 X, which are indented with two tabs.
  • Continue this pattern until all elements in the list L have been processed.

My solution

1
2
3
4
5
6
7
8
9
10
11
12
13
def f1():
while True:
print(" /\\")
print("/ \\")
yield
print("----")
yield
print("\\ /")
print(" \\/")
yield
print(" ||")
print(" ||")
yield

Standard solution

1
2
3
4
5
6
7
8
9
10
def f1():
while True:
print(' /\\\n/ \\')
yield
print('----')
yield
print('\\ /\n \\/')
yield
print(' ||\n ||')
yield

Exercise 2

Problem Description

1
2
3
4
5
6
7
Line:   [1,   3,   3,   1]
| | |
i=0 i=1 i=2
↓ ↓ ↓
Calculate:1+3 3+3 3+1
↓ ↓ ↓
Results: 4 6 4

My solution

1
2
3
4
5
6
7
def f2():
# initialized
row = [1]
while True:
yield row
# row[i] + row[i + 1] is the expression in list comprehension
row = [1] + [row[i] + row[i + 1] for i in range(len(row)-1)] + [1]

Standard solution

1
2
3
4
5
6
def f2():
L = [1]
yield L
while True:
L = [1] + [L[i] + L[i + 1] for i in range(len(L) - 1)] + [1]
yield L

Exercise 3

Problem Description

First, we need to understand the requirements of the problem:

Input: A list of integers L, such as [n1, n2, n3, …]. Output:

  • Output n1 lines of rank 1 X, which means X without indentation.
  • Between each pair of rank 1 X, output n2 lines of rank 2 X, which are indented with one tab (\t).
  • Between each pair of rank 2 X, output n3 lines of rank 3 X, which are indented with two tabs.
  • Continue this pattern until all elements in the list L have been processed.

My solution

1
2
3
4
5
6
7
8
9
10
def f3(L):
def helper(L, rank):
if not L:
return
n = L[0]
for i in range(n):
print('\t' * rank + 'X')
if i < n - 1:
helper(L[1:], rank + 1)
helper(L, 0)

We need to write a recursive function to handle this nested structure. Here is the implementation idea:

Define a helper function helper(L, rank), where:

  • L is the list currently being processed.
  • rank is the current level (indentation level).

In the helper function:

  1. If the list is empty, return directly.
  2. Get the number of lines to output at the current level, n = L[0].
  3. Use a loop to output n lines of the current level’s X, with each line having the corresponding number of tab characters (\t) based on the rank.
  4. After each output, if it is not the last element and it is not the last line, recursively call helper to handle the next level of X lines.

Standard solution

1
2
3
4
5
6
7
8
9
10
11
12

def f3(L):
_f3(L, 0)

def _f3(L, n):
if n == len(L):
return
for _ in range(L[n] - 1):
print('\t' * n, 'X', sep='')
_f3(L, n + 1)
if L[n]:
print('\t' * n, 'X', sep='')

Exercise 4

Problem Description

Objective: Given a list L of integers, we need to:

  • Break it down into sublists of equal length, where the length is maximal.
  • This process is recursive: each sublist is further broken down in the same manner.
  • The original list should be preserved (i.e., not modified).

Key Points:

  • We aim to split the list into the largest possible sublists of equal length (greater than 1) that evenly divide the length of the list.
  • The recursion continues until a sublist cannot be broken down further (i.e., its length cannot be divided into equal parts greater than 1).

My solution

1
2
3
4
5
6
7
8
9
10
11
from math import sqrt

def f4(L):
n = len(L)
# Try to find the maximal sublist length greater than 1
for sublist_length in range(n // 2, 1, -1):
if n % sublist_length == 0:
# Split the list into sublists
sublists = [f4(L[i:i + sublist_length]) for i in range(0, n, sublist_length)]
return sublists
return L.copy()

Standard solution

1
2
3
4
5
6
7

def f4(L):
for n in range(2, round(sqrt(len(L))) + 1):
if len(L) % n == 0:
w = len(L) // n
return [f4(L[i : i + w]) for i in range(0, len(L), w)]
return list(L)

Exercise 5

Problem Description

Objective: Given a special list L (a list whose elements are integers or other special lists), we need to return a dictionary where:

Keys are tuples of indices (i_1, i_2, ..., i_n).
Values are integers e.
The keys represent the path of indices to reach an integer e within the nested list structure.

Interpretation:

  • If the key is (i_1,), then L[i_1] is an integer e.
  • If the key is (i_1, i_2), then L[i_1][i_2] is an integer e.
  • If the key is (i_1, i_2, i_3), then L[i_1][i_2][i_3] is an integer e.
  • And so on.

Constraints:

We need to handle any depth of nesting.
Use isinstance() to check if an element is an integer or a list.
The original list should not be modified.

My solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def f5(L):
result = {}
def helper(sublist, path):
for index, element in enumerate(sublist):
current_path = path + (index,)
if isinstance(element, int):
result[current_path] = element
elif isinstance(element, list):
helper(element, current_path)
else:
# If there are other types, you might want to handle them or raise an error
pass
helper(L, ())
return result

Standard solution

1
2
3
4
5
6
7
8
9
10
def f5(L):
D = {}
for i in range(len(L)):
if isinstance(L[i], int):
D[(i,)] = L[i]
else:
E = f5(L[i])
for s in E:
D[i, *s] = E[s]
return D

Difference between my solution and the standard solution

My Solution

  • Uses a Helper Function (helper): Your solution defines an inner function helper(sublist, path) to handle the recursion.
  • Explicit Path Passing: The path variable, representing the current position in the nested list, is explicitly passed and updated at each recursive call.
  • State Encapsulation: By using a nested function, the state (path and result) is encapsulated within the f5 function’s scope.
    Standard Solution
  • Direct Recursion: The standard solution directly calls f5(L[i]) recursively without using a helper function.
  • Implicit Path Construction: It constructs the path by combining the current index i with the indices from the recursive call (s) using tuple unpacking.
  • Dictionary Merging: After each recursive call, it merges the returned dictionary E into the current dictionary D.

Exercise 6

Problem Description

The given problem is not strictly about the Fibonacci sequence, but it generalizes similar principles to a broader set of recursive relationships. In the problem, you’re asked to compute the n-th term of a series, where the series is defined by some initial terms (first_terms) and a set of recurrence factors (factors). The Fibonacci sequence is a special case of such a recurrence relation, where each term is the sum of the two preceding terms.

My solution(Wrong)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

def f6(first_terms, factors, n):
k = len(first_terms) - 1
sequence = first_terms.copy()

# If n is within the initial terms, return it directly
if n <= k:
return sequence[n]

# Compute terms iteratively up to n
for i in range(k + 1, n + 1):
xi = 0
for s in range(k + 1):
xi += factors[s] * sequence[i - k - 1 + s]
sequence.append(xi)

return sequence[n]

Standard solution

1
2
3
4
5
6
7
8
9
10
11
12
13
def f6(first_terms, factors, n):
series = {i: first_terms[i] for i in range(len(first_terms))}
_f6(factors, n, series)
return series[n]

def _f6(factors, n, series):
if n in series:
return
x = 0
for i in range(1, len(factors) + 1):
_f6(factors, n - i, series)
x += factors[-i] * series[n - i]
series[n] = x
  • Python
  • Answer
  • 9021
  • Tutorial

show all >>

9021_TUT_6

  2025-03-02
字数统计: 2.7k字   |   阅读时长: 16min

Exercise 1

Problem description

It is hard to find the pattern of the input and output. But through this example:

1
2
3
4
5
6
statements = 'from exercise_6_1 import f1; '\
'L = [[4, 8], [6, 3, 0], [7]]; print(f1(L))'

%%run_and_test python3 -c "$statements"

'([0, 3, 4, 6, 7, 8], [[0, 3], [4, 6, 7], [8]])\n'

We need to sort the elements into a one-dimensional list and then split them according to the lengths of the sublists in the input.

My Solution

So, the function f1 should be like this:

1
2
3
4
5
6
7
8
9
10
11
12
def f1(L):
# Calculate the length of each sublist
lengths = [len(i) for i in L]
# Split the list into a one-dimensional list
P = [i for j in L for i in j]
P.sort()
ans = []
flag = 0
for i in range(len(L)):
ans.append(P[flag: flag + lengths[i]])
flag += lengths[i]
return P, ans

Standard Solution

1
2
3
4
5
6
7
8
def f1(L):
lengths = [len(L1) for L1 in L]
F = sorted(e for L1 in L for e in L1)
R = []
i = 0
for n in range(len(L)):
R.append(F[i : (i := i + lengths[n])])
return F, R

Explanation

The difference between the two solutions is that the standard solution uses the walrus operator := to simplify the code. The walrus operator is a new feature in Python 3.8. It is used to assign a value to a variable as part of an expression. It is useful when you want to assign a value to a variable and use that value in the same expression.

Exercise 2

Problem description

This exercise ask us to output the sum of diagonal elements of a matrix. Based on the line where i, j are located.
We can use Numpy to solve this problem. It has a function numpy.diagonal() that can return the diagonal elements of a matrix.

My Solution

1
2
3
4
5
6
7
8
9
10
11
12
import numpy as np

def f2(L, i, j, major=True):
# Convert L to a numpy array for easier indexing
L = np.array(L)
i -= 1
j -= 1
if major:
dia = sum(np.diag(L, k= j - i))
else:
dia = sum(np.diag(np.fliplr(L), k= (len(L) - 1 - j) - i))
return dia

Explanation

In a normal matrix, the np.diag() function allows extracting diagonals at different offsets, where the offset k=0 represents the main diagonal.

When we flip the matrix, the coordinates for each cell change in such a way that we need to adjust our calculation of the offset accordingly.

Specifically, for a matrix of size n, flipping it means that the column index j of the original matrix transforms to (n - 1 - j) in the flipped version. Hence, when we want to calculate the offset of the diagonal that passes through (i, j) in the original matrix, we need to determine the offset using (n - 1 - j) - i to correctly represent the position in the flipped version.

Standard Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def f2(L, i, j, major=True):
i1 = i = i - 1
j1 = j = j - 1
the_sum = L[i1][j1]
if major:
while (i1 := i1 - 1) >= 0 and (j1 := j1 - 1) >= 0:
the_sum += L[i1][j1]
i1, j1 = i, j
while (i1 := i1 + 1) < len(L) and (j1 := j1 + 1) < len(L):
the_sum += L[i1][j1]
else:
while (i1 := i1 - 1) >= 0 and (j1 := j1 + 1) < len(L):
the_sum += L[i1][j1]
i1, j1 = i, j
while (i1 := i1 + 1) < len(L) and (j1 := j1 - 1) >= 0:
the_sum += L[i1][j1]
return the_sum

Explanation

The standard solution uses a while loop to iterate through the elements of the matrix based on the given indices i and j. It calculates the sum of the diagonal elements by moving in the specified direction (major or minor diagonal) and updating the sum accordingly.

Exercise 3

Problem description

This exercise involves swapping elements in a square matrix with an even side length such that each element in a specified half (‘top’, ‘bottom’, ‘left’, or ‘right’) is at least equal to its diagonally opposite element.

If the half is ‘top’ or ‘left’, the element in that half should be at least equal to the diagonally opposite one, otherwise they should be swapped. Conversely, if the half is ‘bottom’ or ‘right’, the element in the other half should be swapped if it is greater.

Standard Solution

The solution is implemented by creating a copy of the matrix and then iterating over the relevant half to determine if swapping is necessary based on the given criteria.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# Assume that the argument L is a list of lists of integers
# that displays as a square with an even side length
# and the argument half is one of
# 'top', 'bottom', 'left' or 'right'.

# Possibly swaps elements that are diagonally opposite
# so that the element in the "half" part of the square is
# at least equal to the other element.

def f3(L, half='top'):
# Create a copy of the original list to avoid modifying it directly
L1 = [list(row) for row in L]
n = len(L)

# Define ranges based on the specified half
ranges = {
'top': (range(n // 2), range(n)),
'bottom': (range(n // 2, n), range(n)),
'left': (range(n), range(n // 2)),
'right': (range(n), range(n // 2, n))
}

# Iterate through the specified half and possibly swap diagonally opposite elements
for i in ranges[half][0]:
for j in ranges[half][1]:
if L[i][j] < L[-i - 1][-j - 1]:
L1[i][j], L1[-i - 1][-j - 1] = L1[-i - 1][-j - 1], L1[i][j]

return L1

Explanation

The approach in my solution uses a dictionary to define the ranges for each half of the matrix. By iterating over these ranges, it ensures that only the elements in the specified half are considered. If the element in the specified half is smaller than its diagonally opposite counterpart, they are swapped.

The ranges dictionary defines which rows and columns are to be iterated based on the specified half:

  • 'top' considers the top half of the matrix (rows from 0 to n//2)
  • 'bottom' considers the bottom half of the matrix (rows from n//2 to n)
  • 'left' considers the left half of the matrix (columns from 0 to n//2)
  • 'right' considers the right half of the matrix (columns from n//2 to n)

The values are swapped only if they do not meet the condition defined for the given half.

Exercise 4

Problem description

This exercise involves creating a visual pattern on an n x n grid using two different characters to represent black and white cells. The argument n is an integer at least equal to 1, and the argument black_center is either True or False, which influences the color of the center of the grid. The function should use np.full() to create the grid and modify it using simple loops without nested loops.

The function prints the grid instead of returning it.

My Solution

The solution starts by creating an n x n grid initialized entirely to either black or white, depending on whether the center should be black or not. The function then iteratively adjusts concentric squares within the grid, switching the colors as needed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import numpy as np

def black(i, black_centre):
return (i % 4 in {1, 2}) ^ (not black_centre)

def f4(n, black_centre=True):
# Create the initial grid based on the color of the center
grid = np.full((n, n), '⬛') if black(n, black_centre)\
else np.full((n, n), '⬜')
# Adjust concentric squares within the grid
for i in range(1, n // 2 + 1):
grid[i : n - i, i : n - i] = '⬛' if black(n - 2 * i, black_centre)\
else '⬜'
# Print the grid row by row
for row in grid:
print(*row, sep='')

Explanation

  • The function black(i, black_centre) determines the color of the concentric square based on the current size i and whether the center should be black (black_centre argument).
  • The np.full() function is used to create the grid, either filled with black (‘⬛’) or white (‘⬜’), depending on the value of black(n, black_centre), which determines the color of the center.
  • The for loop iterates through half of the matrix (n // 2), adjusting each concentric square layer by layer.
    • The slicing operation grid[i : n - i, i : n - i] selects the relevant inner square and fills it with either black or white, alternating colors as determined by black(n - 2 * i, black_centre).
  • Finally, the grid is printed row by row, with each character printed consecutively for easy visualization.

Standard Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import numpy as np

def black(i, black_centre):
return (i % 4 in {1, 2}) ^ (not black_centre)

def f4(n, black_centre=True):
# Create the grid, starting with the initial color based on the center
grid = np.full((n, n), '⬛' if black(n, black_centre) else '⬜')
# Iterate to adjust each concentric layer
for i in range(1, n // 2 + 1):
color = '⬛' if black(n - 2 * i, black_centre) else '⬜'
grid[i:n-i, i:n-i] = color
# Print the grid
for row in grid:
print(''.join(row))

Explanation

The standard solution follows a similar approach to my solution but uses slightly different syntax to achieve the same result:

  • The grid is created using np.full(), just like in my solution.
  • The loop iterates through each concentric layer, updating the color accordingly. The variable color is used to determine what color each inner square should be, making the assignment more readable.
  • The grid is printed in a slightly different way by joining the row’s elements into a single string (print(''.join(row))). This makes the output cleaner by removing the spaces between characters, which is purely aesthetic.

The primary differences between my solution and the standard solution are the use of inline expressions for color selection and minor differences in how the final output is formatted when printed.

Exercise 5

Try it!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import numpy as np

# Define a 2D NumPy array (matrix)
array = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])

# Sum all elements
total_sum = np.sum(array) # Output: 45

# Sum along rows (axis=1)
row_sums = np.sum(array, axis=1) # Output: [6, 15, 24]

# Sum along columns (axis=0)
column_sums = np.sum(array, axis=0) # Output: [12, 15, 18]

print(total_sum, row_sums, column_sums)

Problem description

The exercise is to compute the sum of elements in a given row and column and display a colored square at their intersection based on the sign of the sum:

  • A blue square (‘🟦’) if the sum is 0.
  • A green square (‘🟩’) if the sum is strictly positive.
  • A red square (‘🟥’) if the sum is strictly negative.

For example, given a list of lists representing the matrix:

1
2
3
.  .  2  .  .
2 0 -3 7 -4
. . -4 . .

The function should compute the sum for each row and column, and update the intersection elements accordingly.

The function should use np.sum() and np.sign() to simplify calculations, and should use at most loops within loops, avoiding deeper nesting.

The output is printed, not returned.

My Solution

The solution uses NumPy to calculate the sum of each row and column. The function then iterates through the given list and determines the sign of the sum at each intersection, displaying the appropriate color.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import numpy as np

def f5(L):
# Convert L to a numpy array for easier manipulation
L = np.array(L)
n_rows, n_cols = L.shape

# Calculate row and column sums
row_sums = np.sum(L, axis=1)
col_sums = np.sum(L, axis=0)

# Iterate through the list and determine the color for each intersection
for i in range(n_rows):
row = []
for j in range(n_cols):
intersection_sum = row_sums[i] + col_sums[j] - L[i][j] # Avoid double counting L[i][j]
if intersection_sum == 0:
row.append('🟦') # Blue square
elif intersection_sum > 0:
row.append('🟩') # Green square
else:
row.append('🟥') # Red square
print(''.join(row))

# Test with the provided statements
statements = 'from exercise_6_5 import f5; '\
'f5([[4, -6, -5], [6, -7, 6]])'

%%run_and_test python3 -c "$statements"

'🟥🟥🟥\n'
'🟩🟥🟦\n'

Explanation

  • NumPy Conversion: The list L is converted to a NumPy array to take advantage of the efficient np.sum() function for computing sums.
  • Row and Column Sums: The sums for rows and columns are computed separately using np.sum() with the appropriate axis argument.
  • Iteration for Colors: The function then iterates over each element in the matrix to determine the color of the intersection based on the calculated row and column sums. The color is chosen as follows:
    • '🟦' (blue) for a sum of 0.
    • '🟩' (green) for a strictly positive sum.
    • '🟥' (red) for a strictly negative sum.
  • Output: Finally, the grid is printed row by row.

Standard Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import numpy as np

def f5(L):
L1 = np.array(L)
for i in range(L1.shape[0]):
for j in range(L1.shape[1]):
L[i][j] = { 0: '🟦',
1: '🟩',
-1: '🟥'
}[np.sign(np.sum(L1[i, :])
+ np.sum(L1[: , j])
- L1[i, j]
)
]
for row in L:
print(*row, sep='')

Explanation

The standard solution follows a similar approach to my solution but uses a dictionary to map the sign of the sum to the corresponding color. The np.sign() function is used to determine the sign of the sum, and the dictionary is used to select the appropriate color based on the sign.

Exercise 6

Problem description

This exercise is similar to Exercise 5, where we compute the sum of elements in a given row and column and display a colored square at their intersection based on the sign of the sum:

  • A blue square (‘🟦’) if the sum is 0.
  • A green square (‘🟩’) if the sum is strictly positive.
  • A red square (‘🟥’) if the sum is strictly negative.

However, the main differences are:

  • Input Structure: The list L must be a square matrix (i.e., the number of rows equals the number of columns).
  • Optimization Requirement: The solution for f6 must avoid manual loops for computation (except for output). Instead, the solution must use vectorized operations in NumPy to enhance computational efficiency.
  • Advanced NumPy Usage: The exercise explicitly suggests using advanced NumPy functions such as np.vectorize(), np.newaxis, and broadcasting.

The output is printed, not returned.

My Solution

The solution for f6 uses advanced NumPy techniques to fully vectorize the computations without using explicit loops (except for printing). It creates the required colored grid by computing the sum for each row and column and then using a vectorized mapping to determine the color.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import numpy as np

def f6(L):
L1 = np.array(L)
# Vectorize the color selection based on the sum of row and column
for row in np.vectorize(lambda e: { 0: '🟦',
1: '🟩',
-1: '🟥'
}[e]
)(np.sign(np.sum(L1, axis=1)[:, np.newaxis]
+ np.sum(L1.T, axis=1)
- L1
)
):
print(*row, sep='')

Explanation

  • Vectorization: The solution uses np.vectorize() to apply a function element-wise over an array, effectively mapping each sum to a specific color.
  • Avoiding Loops: Instead of iterating through each element manually, the solution leverages NumPy’s vectorization to calculate row and column sums simultaneously.
  • Broadcasting and np.newaxis: The use of [:, np.newaxis] helps in broadcasting the row sums across the columns to facilitate efficient addition with the column sums. This allows the entire computation to be performed without explicit looping.
  • Output: Finally, each row is printed with the appropriate color, similar to Exercise 5.

Key Differences from Exercise 5

  1. Input Structure: Exercise 5 allows any list of lists, whereas Exercise 6 requires a square matrix.
  2. Loop Usage: Exercise 5 uses explicit loops to calculate intersection sums, whereas Exercise 6 relies entirely on vectorized operations and avoids manual loops (except for printing).
  3. Efficiency: Exercise 6 is more computationally efficient, as it is designed to handle larger datasets using optimized NumPy operations, whereas Exercise 5 uses loops that can be slower for large matrices.
  • Python
  • 9021

show all >>

123…15下一页 >>

150 篇 | 131.7k
次 | 人
这里自动载入天数这里自动载入时分秒
2022-2025 loong loong | 新南威尔士龙龙号