Compare commits
8 Commits
848235fc8a
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| fc42369e54 | |||
| 7a329c639e | |||
| 360d2c3d21 | |||
| 063f3277bb | |||
| 4cb625ab9c | |||
| eda3bf691a | |||
| 233c53fb9d | |||
| cae7127193 |
@@ -1,4 +1,4 @@
|
||||
# Analysis #04 — Reverse Linked List: Academic Exercise
|
||||
# #04 — Reverse Linked List: Academic Exercise
|
||||
|
||||
## Problem
|
||||
|
||||
@@ -253,3 +253,8 @@ It remains one of the most common classic coding interview questions.
|
||||
The exercise is valuable for learning pointer manipulation.
|
||||
|
||||
Its usefulness as a predictor of engineering ability is far less obvious.
|
||||
|
||||
|
||||
## A More Engineering-Oriented Alternatives
|
||||
|
||||
If the goal is to eveluate pointer manipulation, linked-list traversal and in-place node relocation, a message queue partitioning task may provide a more realistic engineering scenario. This is idea for #05
|
||||
@@ -1,282 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 9.0.2, 2026-06-18T21:29:58. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
<value type="QByteArray">{b2bd4c5a-81ed-4a10-b603-28664c066c48}</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.ActiveTarget</variable>
|
||||
<value type="qlonglong">0</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.EditorSettings</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
|
||||
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
|
||||
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
|
||||
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
|
||||
<value type="QString" key="language">Cpp</value>
|
||||
<valuemap type="QVariantMap" key="value">
|
||||
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
|
||||
<value type="QString" key="language">QmlJS</value>
|
||||
<valuemap type="QVariantMap" key="value">
|
||||
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="EditorConfiguration.CodeStyle.Count">2</value>
|
||||
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
|
||||
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
|
||||
<value type="int" key="EditorConfiguration.IndentSize">4</value>
|
||||
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
|
||||
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
|
||||
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
|
||||
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
|
||||
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
|
||||
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
|
||||
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
|
||||
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
|
||||
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
|
||||
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
|
||||
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
|
||||
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
|
||||
<value type="int" key="EditorConfiguration.TabSize">8</value>
|
||||
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
|
||||
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
|
||||
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
|
||||
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
|
||||
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
|
||||
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
|
||||
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
|
||||
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
|
||||
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
|
||||
<value type="bool" key="EditorConfiguration.tintMarginArea">true</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.PluginSettings</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
|
||||
<value type="bool" key="AutoTest.Framework.Boost">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.CTest">false</value>
|
||||
<value type="bool" key="AutoTest.Framework.Catch">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.GTest">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
|
||||
<value type="int" key="AutoTest.RunAfterBuild">0</value>
|
||||
<value type="bool" key="AutoTest.UseGlobal">true</value>
|
||||
<valuemap type="QVariantMap" key="ClangTools">
|
||||
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
|
||||
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
|
||||
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
|
||||
<value type="int" key="ClangTools.ParallelJobs">2</value>
|
||||
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
|
||||
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
|
||||
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
|
||||
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
|
||||
</valuemap>
|
||||
<value type="QDateTime" key="GitLab.LastRequest"></value>
|
||||
<value type="QString" key="GitLab.LinkedId"></value>
|
||||
<value type="QString" key="GitLab.Project"></value>
|
||||
<value type="QString" key="GitLab.Server"></value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Target.0</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="QString" key="DeviceType">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{f6fcd567-e933-4810-b758-788e32d93b83}</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
|
||||
<value type="int" key="EnableQmlDebugging">0</value>
|
||||
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/tmp/build-reverse-linked-list-Desktop-Debug</value>
|
||||
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/tmp/build-reverse-linked-list-Desktop-Debug</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
|
||||
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
|
||||
<valuelist type="QVariantList" key="QtProjectManager.QMakeBuildStep.SelectedAbis"/>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
|
||||
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Debug</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
|
||||
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">2</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
|
||||
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/tmp/build-reverse-linked-list-Desktop-Release</value>
|
||||
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/tmp/build-reverse-linked-list-Desktop-Release</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
|
||||
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
|
||||
<valuelist type="QVariantList" key="QtProjectManager.QMakeBuildStep.SelectedAbis"/>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
|
||||
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Release</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
|
||||
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.2">
|
||||
<value type="int" key="EnableQmlDebugging">0</value>
|
||||
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/tmp/build-reverse-linked-list-Desktop-Profile</value>
|
||||
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/tmp/build-reverse-linked-list-Desktop-Profile</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
|
||||
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
|
||||
<valuelist type="QVariantList" key="QtProjectManager.QMakeBuildStep.SelectedAbis"/>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
|
||||
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Profile</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
|
||||
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
|
||||
<value type="int" key="SeparateDebugInfo">0</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">3</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
|
||||
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
|
||||
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
|
||||
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
|
||||
<value type="QString">0</value>
|
||||
<value type="QString">1</value>
|
||||
<value type="QString">2</value>
|
||||
<value type="QString">3</value>
|
||||
<value type="QString">4</value>
|
||||
<value type="QString">5</value>
|
||||
<value type="QString">6</value>
|
||||
<value type="QString">7</value>
|
||||
<value type="QString">8</value>
|
||||
<value type="QString">9</value>
|
||||
<value type="QString">10</value>
|
||||
<value type="QString">11</value>
|
||||
<value type="QString">12</value>
|
||||
<value type="QString">13</value>
|
||||
<value type="QString">14</value>
|
||||
</valuelist>
|
||||
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:/home/deeaitch/work/3.projects/git.himro.ca/library/beyond-interviews/analysis/04-reverse-linked-list/exapmle/reverse-linked-list/reverse-linked-list.pro</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/deeaitch/work/3.projects/git.himro.ca/library/beyond-interviews/analysis/04-reverse-linked-list/exapmle/reverse-linked-list/reverse-linked-list.pro</value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/tmp/build-reverse-linked-list-Desktop-Debug</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.TargetCount</variable>
|
||||
<value type="qlonglong">1</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
|
||||
<value type="int">22</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>Version</variable>
|
||||
<value type="int">22</value>
|
||||
</data>
|
||||
</qtcreator>
|
||||
74
analysis/05-Message_Queue_Partitioning/example/message_queue_partitioning/.gitignore
vendored
Normal file
74
analysis/05-Message_Queue_Partitioning/example/message_queue_partitioning/.gitignore
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
# This file is used to ignore files which are generated
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
*~
|
||||
*.autosave
|
||||
*.a
|
||||
*.core
|
||||
*.moc
|
||||
*.o
|
||||
*.obj
|
||||
*.orig
|
||||
*.rej
|
||||
*.so
|
||||
*.so.*
|
||||
*_pch.h.cpp
|
||||
*_resource.rc
|
||||
*.qm
|
||||
.#*
|
||||
*.*#
|
||||
core
|
||||
!core/
|
||||
tags
|
||||
.DS_Store
|
||||
.directory
|
||||
*.debug
|
||||
Makefile*
|
||||
*.prl
|
||||
*.app
|
||||
moc_*.cpp
|
||||
ui_*.h
|
||||
qrc_*.cpp
|
||||
Thumbs.db
|
||||
*.res
|
||||
*.rc
|
||||
/.qmake.cache
|
||||
/.qmake.stash
|
||||
|
||||
# qtcreator generated files
|
||||
*.pro.user*
|
||||
CMakeLists.txt.user*
|
||||
|
||||
# xemacs temporary files
|
||||
*.flc
|
||||
|
||||
# Vim temporary files
|
||||
.*.swp
|
||||
|
||||
# Visual Studio generated files
|
||||
*.ib_pdb_index
|
||||
*.idb
|
||||
*.ilk
|
||||
*.pdb
|
||||
*.sln
|
||||
*.suo
|
||||
*.vcproj
|
||||
*vcproj.*.*.user
|
||||
*.ncb
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.vcxproj
|
||||
*vcxproj.*
|
||||
|
||||
# MinGW generated files
|
||||
*.Debug
|
||||
*.Release
|
||||
|
||||
# Python byte code
|
||||
*.pyc
|
||||
|
||||
# Binaries
|
||||
# --------
|
||||
*.dll
|
||||
*.exe
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
|
||||
struct Message {
|
||||
std::uint32_t id;
|
||||
bool retry;
|
||||
Message *next;
|
||||
};
|
||||
|
||||
struct MessageQueue {
|
||||
Message *head;
|
||||
Message *tail;
|
||||
};
|
||||
|
||||
struct PartitionResult {
|
||||
MessageQueue ready;
|
||||
MessageQueue retry;
|
||||
};
|
||||
|
||||
static void append (MessageQueue &queue, Message *message) {
|
||||
assert (message != nullptr);
|
||||
assert (message->next == nullptr);
|
||||
|
||||
if (queue.tail == nullptr) {
|
||||
queue.head = message;
|
||||
queue.tail = message;
|
||||
return;
|
||||
}
|
||||
|
||||
queue.tail->next = message;
|
||||
queue.tail = message;
|
||||
}
|
||||
|
||||
PartitionResult partition_messages (MessageQueue &source) {
|
||||
PartitionResult result{
|
||||
{nullptr, nullptr},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
Message *current = source.head;
|
||||
|
||||
/*
|
||||
* The source queue is consumed by this operation.
|
||||
*
|
||||
* Clearing it before traversal makes the ownership transfer explicit:
|
||||
* every node taken from the original queue must be appended to exactly
|
||||
* one of the two result queues.
|
||||
*/
|
||||
source.head = nullptr;
|
||||
source.tail = nullptr;
|
||||
|
||||
while (current != nullptr) {
|
||||
/*
|
||||
* Save the traversal link before modifying current->next.
|
||||
* The same intrusive link is reused by the destination queue.
|
||||
*/
|
||||
Message *next = current->next;
|
||||
current->next = nullptr;
|
||||
|
||||
if (current->retry)
|
||||
append (result.retry, current);
|
||||
else
|
||||
append (result.ready, current);
|
||||
|
||||
current = next;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void print_queue (const char *name, const MessageQueue &queue) {
|
||||
std::cout << name << ": ";
|
||||
|
||||
const Message *current = queue.head;
|
||||
|
||||
if (current == nullptr) {
|
||||
std::cout << "<empty>\n";
|
||||
return;
|
||||
}
|
||||
|
||||
while (current != nullptr) {
|
||||
std::cout << current->id;
|
||||
|
||||
if (current->next != nullptr)
|
||||
std::cout << " -> ";
|
||||
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
std::cout << '\n';
|
||||
}
|
||||
|
||||
static std::size_t queue_size (const MessageQueue &queue) {
|
||||
std::size_t size = 0;
|
||||
const Message *current = queue.head;
|
||||
|
||||
while (current != nullptr) {
|
||||
++size;
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static void verify_queue (const MessageQueue &queue) {
|
||||
if (queue.head == nullptr) {
|
||||
assert (queue.tail == nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
assert (queue.tail != nullptr);
|
||||
assert (queue.tail->next == nullptr);
|
||||
|
||||
const Message *current = queue.head;
|
||||
|
||||
while (current->next != nullptr)
|
||||
current = current->next;
|
||||
|
||||
assert (current == queue.tail);
|
||||
}
|
||||
|
||||
int main() {
|
||||
Message a{1U, false, nullptr};
|
||||
Message b{2U, true, nullptr};
|
||||
Message c{3U, false, nullptr};
|
||||
Message d{4U, true, nullptr};
|
||||
|
||||
a.next = &b;
|
||||
b.next = &c;
|
||||
c.next = &d;
|
||||
|
||||
MessageQueue outgoing{&a, &d};
|
||||
|
||||
std::cout << "Before partition\n";
|
||||
print_queue ("Outgoing", outgoing);
|
||||
|
||||
const PartitionResult result = partition_messages (outgoing);
|
||||
|
||||
std::cout << "\nAfter partition\n";
|
||||
print_queue ("Outgoing", outgoing);
|
||||
print_queue ("Ready", result.ready);
|
||||
print_queue ("Retry", result.retry);
|
||||
|
||||
verify_queue (outgoing);
|
||||
verify_queue (result.ready);
|
||||
verify_queue (result.retry);
|
||||
|
||||
assert (outgoing.head == nullptr);
|
||||
assert (outgoing.tail == nullptr);
|
||||
|
||||
assert (result.ready.head == &a);
|
||||
assert (result.ready.tail == &c);
|
||||
assert (a.next == &c);
|
||||
assert (c.next == nullptr);
|
||||
|
||||
assert (result.retry.head == &b);
|
||||
assert (result.retry.tail == &d);
|
||||
assert (b.next == &d);
|
||||
assert (d.next == nullptr);
|
||||
|
||||
assert (queue_size (result.ready) + queue_size (result.retry) == 4U);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
TEMPLATE = app
|
||||
CONFIG += console c++17
|
||||
CONFIG -= app_bundle
|
||||
CONFIG -= qt
|
||||
|
||||
SOURCES += \
|
||||
main.cpp
|
||||
@@ -0,0 +1,25 @@
|
||||
@startuml
|
||||
|
||||
rectangle "Traversal"
|
||||
|
||||
() previous
|
||||
() current
|
||||
() next
|
||||
|
||||
previous --> current
|
||||
current --> next
|
||||
|
||||
note right
|
||||
|
||||
save next
|
||||
|
||||
detach current
|
||||
|
||||
append to
|
||||
destination queue
|
||||
|
||||
continue with next
|
||||
|
||||
end note
|
||||
|
||||
@enduml
|
||||
BIN
analysis/05-Message_Queue_Partitioning/example/uml/iteration.png
Normal file
BIN
analysis/05-Message_Queue_Partitioning/example/uml/iteration.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,13 @@
|
||||
@startuml
|
||||
|
||||
[*] --> Outgoing
|
||||
|
||||
Outgoing --> Sent : success
|
||||
|
||||
Outgoing --> Retry : retry == true
|
||||
|
||||
Retry --> Outgoing : rescheduled
|
||||
|
||||
Sent --> [*]
|
||||
|
||||
@enduml
|
||||
BIN
analysis/05-Message_Queue_Partitioning/example/uml/loop.png
Normal file
BIN
analysis/05-Message_Queue_Partitioning/example/uml/loop.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -0,0 +1,17 @@
|
||||
@startuml
|
||||
|
||||
rectangle "Outgoing Queue" as Q1
|
||||
rectangle "Ready Queue" as Q2
|
||||
rectangle "Retry Queue" as Q3
|
||||
|
||||
Q1 --> Q2 : move node
|
||||
Q1 --> Q3 : move node
|
||||
|
||||
note bottom
|
||||
|
||||
Every message belongs
|
||||
to exactly one queue.
|
||||
|
||||
end note
|
||||
|
||||
@enduml
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,39 @@
|
||||
@startuml
|
||||
|
||||
left to right direction
|
||||
skinparam linetype ortho
|
||||
skinparam shadowing false
|
||||
|
||||
package "Before Partition" {
|
||||
rectangle "A\nretry = false" as A
|
||||
rectangle "B\nretry = true" as B
|
||||
rectangle "C\nretry = false" as C
|
||||
rectangle "D\nretry = true" as D
|
||||
|
||||
A --> B : next
|
||||
B --> C : next
|
||||
C --> D : next
|
||||
}
|
||||
|
||||
package "After Partition" {
|
||||
package "Ready Queue" {
|
||||
rectangle "A" as ReadyA
|
||||
rectangle "C" as ReadyC
|
||||
|
||||
ReadyA --> ReadyC : next
|
||||
}
|
||||
|
||||
package "Retry Queue" {
|
||||
rectangle "B" as RetryB
|
||||
rectangle "D" as RetryD
|
||||
|
||||
RetryB --> RetryD : next
|
||||
}
|
||||
}
|
||||
|
||||
A ..> ReadyA : move
|
||||
B ..> RetryB : move
|
||||
C ..> ReadyC : move
|
||||
D ..> RetryD : move
|
||||
|
||||
@enduml
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
@@ -0,0 +1,20 @@
|
||||
@startuml
|
||||
|
||||
participant Queue
|
||||
participant Algorithm
|
||||
participant Ready
|
||||
participant Retry
|
||||
|
||||
Queue -> Algorithm : get next node
|
||||
|
||||
alt retry == false
|
||||
|
||||
Algorithm -> Ready : append(node)
|
||||
|
||||
else retry == true
|
||||
|
||||
Algorithm -> Retry : append(node)
|
||||
|
||||
end
|
||||
|
||||
@enduml
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
276
analysis/05-Message_Queue_Partitioning/readme.md
Normal file
276
analysis/05-Message_Queue_Partitioning/readme.md
Normal file
@@ -0,0 +1,276 @@
|
||||
# #05 — Message Queue Partitioning
|
||||
|
||||
## Problem
|
||||
|
||||
A communication subsystem maintains a singly linked intrusive queue of outgoing messages.
|
||||
|
||||
Each message contains a transmission identifier, a retry flag, and a pointer to the next message:
|
||||
|
||||
```cpp
|
||||
struct Message {
|
||||
uint32_t id;
|
||||
bool retry;
|
||||
Message* next;
|
||||
};
|
||||
```
|
||||
|
||||
After a transmission attempt, some messages may need to be retried.
|
||||
|
||||
Partition the original queue into two separate queues:
|
||||
|
||||
- the ready queue, containing messages that do not require another transmission attempt;
|
||||
- the retry queue, containing messages marked for retry.
|
||||
|
||||
The relative order of messages must be preserved in both queues.
|
||||
|
||||
### Requirements
|
||||
|
||||
- no dynamic memory allocation;
|
||||
- no copying of messages;
|
||||
- reuse the existing list nodes;
|
||||
- preserve the original order in both resulting queues;
|
||||
- process the queue in `O(n)` time.
|
||||
|
||||
## Example
|
||||
|
||||
### Input
|
||||
|
||||
```text
|
||||
A -> B -> C -> D
|
||||
```
|
||||
|
||||
```text
|
||||
A: retry = false
|
||||
B: retry = true
|
||||
C: retry = false
|
||||
D: retry = true
|
||||
```
|
||||
|
||||
### Result
|
||||
|
||||
Ready queue
|
||||
|
||||
```text
|
||||
A -> C
|
||||
```
|
||||
|
||||
Retry queue
|
||||
|
||||
```text
|
||||
B -> D
|
||||
```
|
||||
|
||||
The original queue is consumed during the operation, and every message must belong to exactly one of the two resulting queues.
|
||||
|
||||
---
|
||||
|
||||
# Analysis
|
||||
|
||||
At first glance, this looks like another linked list interview problem.
|
||||
|
||||
Traverse the list.
|
||||
|
||||
Check a flag.
|
||||
|
||||
Split the nodes into two lists.
|
||||
|
||||
Complexity: **O(n)**.
|
||||
|
||||
Simple.
|
||||
|
||||
Except this is one of those rare cases where the interview version is surprisingly close to a real engineering task.
|
||||
|
||||
The interesting part is not the algorithm.
|
||||
|
||||
The interesting part is what the algorithm is actually modifying.
|
||||
|
||||
---
|
||||
|
||||
## This Is Not About Two Lists
|
||||
|
||||
Each node already exists.
|
||||
|
||||
```cpp
|
||||
struct Message {
|
||||
uint32_t id;
|
||||
bool retry;
|
||||
Message* next;
|
||||
};
|
||||
```
|
||||
|
||||
No objects are created.
|
||||
|
||||
No objects are destroyed.
|
||||
|
||||
No messages are copied.
|
||||
|
||||
Only ownership changes.
|
||||
|
||||
The original outgoing queue disappears and every message becomes part of exactly one new queue.
|
||||
|
||||
That small detail changes the entire nature of the problem.
|
||||
|
||||
---
|
||||
|
||||
## The Real Challenge
|
||||
|
||||
The boolean itself is trivial.
|
||||
|
||||
```cpp
|
||||
retry == true
|
||||
```
|
||||
|
||||
is simply a classification.
|
||||
|
||||
The difficult part is maintaining the integrity of two intrusive queues while consuming a third one.
|
||||
|
||||
Every processed node must satisfy one invariant:
|
||||
|
||||
- belong to exactly one queue;
|
||||
- never be lost;
|
||||
- never appear twice;
|
||||
- never keep stale links into the original list.
|
||||
|
||||
Most bugs are not caused by the condition.
|
||||
|
||||
They are caused by pointer manipulation.
|
||||
|
||||
---
|
||||
|
||||
## Why Saving `next` Matters
|
||||
|
||||
The same pointer is used for two completely different purposes.
|
||||
|
||||
During traversal:
|
||||
|
||||
```text
|
||||
current -> next
|
||||
```
|
||||
|
||||
is how we reach the remaining nodes.
|
||||
|
||||
After insertion into a new queue:
|
||||
|
||||
```text
|
||||
current -> next
|
||||
```
|
||||
|
||||
becomes part of another list.
|
||||
|
||||
If the original `next` pointer is overwritten before it is saved, the remainder of the queue is simply lost.
|
||||
|
||||
This is one of the classic pitfalls of intrusive containers.
|
||||
|
||||
---
|
||||
|
||||
## Stable Partition
|
||||
|
||||
The requirements also say:
|
||||
|
||||
> preserve order
|
||||
|
||||
That sounds minor.
|
||||
|
||||
It isn't.
|
||||
|
||||
Appending to the head would produce:
|
||||
|
||||
```text
|
||||
D -> B
|
||||
```
|
||||
|
||||
instead of
|
||||
|
||||
```text
|
||||
B -> D
|
||||
```
|
||||
|
||||
The algorithm therefore performs a **stable partition**, preserving FIFO order in both resulting queues.
|
||||
|
||||
In a communication subsystem this is often essential because later messages may depend on earlier ones.
|
||||
|
||||
---
|
||||
|
||||
## Why No Allocation?
|
||||
|
||||
The requirement
|
||||
|
||||
```text
|
||||
no allocation
|
||||
```
|
||||
|
||||
isn't there to make the problem harder.
|
||||
|
||||
It reflects reality.
|
||||
|
||||
Communication stacks, embedded systems and real-time software often avoid dynamic allocation while processing packets or messages.
|
||||
|
||||
The messages already exist.
|
||||
|
||||
Only their position inside processing queues changes.
|
||||
|
||||
---
|
||||
|
||||
## Hidden Engineering Questions
|
||||
|
||||
The implementation itself is small.
|
||||
|
||||
The engineering questions are not.
|
||||
|
||||
For example:
|
||||
|
||||
- Who owns the original queue after partitioning?
|
||||
- Can another thread append messages during the operation?
|
||||
- Can an interrupt modify the queue?
|
||||
- What happens if the queue is already corrupted?
|
||||
- Can a message belong to multiple intrusive containers?
|
||||
- Should retry count also be updated?
|
||||
- Is there exponential backoff before retrying?
|
||||
|
||||
None of these appear in the problem statement.
|
||||
|
||||
All of them appear in production systems.
|
||||
|
||||
---
|
||||
|
||||
## What This Problem Actually Tests
|
||||
|
||||
Unlike many linked list exercises, this one evaluates something genuinely useful.
|
||||
|
||||
It tests whether a developer can safely manipulate ownership using pointers while preserving structural invariants.
|
||||
|
||||
The algorithm itself is almost secondary.
|
||||
|
||||
Correctness is everything.
|
||||
|
||||
---
|
||||
|
||||
## Key Takeaway
|
||||
|
||||
This is one of the few interview-style linked list problems that has a direct equivalent in production software.
|
||||
|
||||
Not because splitting a list is inherently interesting.
|
||||
|
||||
But because communication stacks, schedulers, networking software and embedded systems continuously reorganize intrusive queues exactly like this.
|
||||
|
||||
The interview version removes most of the surrounding system.
|
||||
|
||||
The engineering version adds ownership, invariants, concurrency and failure handling.
|
||||
|
||||
The pointer operations remain almost identical.
|
||||
|
||||
The responsibility does not.
|
||||
|
||||
---
|
||||
|
||||
## Project Perspective
|
||||
|
||||
> Exists in real engineering?
|
||||
|
||||
**Yes. Frequently.**
|
||||
|
||||
> Exists in interview form?
|
||||
|
||||
**Yes.**
|
||||
|
||||
One of the rare cases where the interview problem remains close to its real-world counterpart.
|
||||
742
analysis/06-The_Myth_of_Clean_Input/readme.md
Normal file
742
analysis/06-The_Myth_of_Clean_Input/readme.md
Normal file
@@ -0,0 +1,742 @@
|
||||
# #06 — The Myth of Clean Input
|
||||
|
||||
## Problem
|
||||
|
||||
Most algorithmic problems begin in roughly the same way:
|
||||
|
||||
> Given an array.
|
||||
|
||||
> Given a linked list.
|
||||
|
||||
> Given a vector of temperatures.
|
||||
|
||||
For example:
|
||||
|
||||
```cpp
|
||||
std::vector<float> temperatures;
|
||||
```
|
||||
|
||||
The task may then ask us to find the maximum value, calculate an average, remove duplicates, or perform some other operation.
|
||||
|
||||
The input is assumed to:
|
||||
|
||||
- already exist;
|
||||
- have the expected type;
|
||||
- use the expected representation;
|
||||
- be free from corruption;
|
||||
- contain values within valid ranges;
|
||||
- be ready for use.
|
||||
|
||||
This assumption feels so natural that it is rarely noticed.
|
||||
|
||||
In real engineering, however, a clean object is rarely the starting point.
|
||||
|
||||
More often, it is the final result of a long processing chain.
|
||||
|
||||
---
|
||||
|
||||
## Typical Interview Thinking
|
||||
|
||||
Consider a simple problem:
|
||||
|
||||
> Find the maximum temperature.
|
||||
|
||||
The candidate receives a ready-to-use container:
|
||||
|
||||
```cpp
|
||||
std::vector<float> temperatures;
|
||||
```
|
||||
|
||||
The solution may be almost trivial:
|
||||
|
||||
```cpp
|
||||
const auto max_temperature =
|
||||
std::max_element(temperatures.begin(), temperatures.end());
|
||||
```
|
||||
|
||||
From there, the discussion may cover:
|
||||
|
||||
- computational complexity;
|
||||
- memory usage;
|
||||
- empty input handling;
|
||||
- use of the standard library;
|
||||
- possible optimizations.
|
||||
|
||||
All attention is focused on the algorithm.
|
||||
|
||||
But one question is almost never asked:
|
||||
|
||||
> Where did this `std::vector<float>` come from?
|
||||
|
||||
Who received the original data?
|
||||
|
||||
Who verified the frame?
|
||||
|
||||
Who determined the format?
|
||||
|
||||
Who converted the raw sensor value into degrees?
|
||||
|
||||
Who decided that the resulting number could be trusted?
|
||||
|
||||
The interview starts with an already prepared object.
|
||||
|
||||
A real system must first create that object.
|
||||
|
||||
---
|
||||
|
||||
## Clean Input Is Not a Starting Condition
|
||||
|
||||
Consider a temperature received from a remote sensor.
|
||||
|
||||
At the business-logic level, it may look like this:
|
||||
|
||||
```text
|
||||
23.7 °C
|
||||
```
|
||||
|
||||
But the system may have originally received nothing more than a sequence of bytes:
|
||||
|
||||
```text
|
||||
02 03 00 1A FF 7C 91 4D
|
||||
```
|
||||
|
||||
Before those bytes can become a temperature, the data must pass through several stages:
|
||||
|
||||
```text
|
||||
UART / CAN / TCP
|
||||
│
|
||||
▼
|
||||
Receive raw bytes
|
||||
│
|
||||
▼
|
||||
Extract a complete frame
|
||||
│
|
||||
▼
|
||||
Validate frame length
|
||||
│
|
||||
▼
|
||||
Verify checksum
|
||||
│
|
||||
▼
|
||||
Check protocol version
|
||||
│
|
||||
▼
|
||||
Deserialize the payload
|
||||
│
|
||||
▼
|
||||
Identify the data source
|
||||
│
|
||||
▼
|
||||
Convert byte order
|
||||
│
|
||||
▼
|
||||
Apply scale and offset
|
||||
│
|
||||
▼
|
||||
Check the physical range
|
||||
│
|
||||
▼
|
||||
Normalized temperature
|
||||
│
|
||||
▼
|
||||
Append to std::vector<float>
|
||||
│
|
||||
▼
|
||||
Find the maximum value
|
||||
```
|
||||
|
||||
The maximum-value algorithm is the final step and may be the simplest step in the entire chain.
|
||||
|
||||
---
|
||||
|
||||
## The Cost of Clean Input
|
||||
|
||||
This declaration looks simple:
|
||||
|
||||
```cpp
|
||||
std::vector<float> temperatures;
|
||||
```
|
||||
|
||||
But that simplicity was not free.
|
||||
|
||||
Before business logic can receive such a container, the system may already have had to:
|
||||
|
||||
- receive data from an external source;
|
||||
- identify message boundaries;
|
||||
- detect corruption;
|
||||
- check protocol-version compatibility;
|
||||
- parse a binary representation;
|
||||
- handle byte order;
|
||||
- apply scaling;
|
||||
- recognize reserved or unavailable values;
|
||||
- verify physical plausibility;
|
||||
- convert the result into an internal representation.
|
||||
|
||||
The algorithm may require one line of code.
|
||||
|
||||
The infrastructure that makes that line meaningful may require thousands.
|
||||
|
||||
This is why business logic is often simpler than the code surrounding it.
|
||||
|
||||
The formula may already be known.
|
||||
|
||||
The algorithm may already exist in the standard library.
|
||||
|
||||
The real work is ensuring that the transition from the external world to the object expected by that algorithm is correct.
|
||||
|
||||
---
|
||||
|
||||
## Normalization: Valid Data Can Still Be Incomparable
|
||||
|
||||
Not every input problem is caused by corruption.
|
||||
|
||||
Sometimes each value is individually valid, but several values represent the same entity in different forms.
|
||||
|
||||
Consider a task that searches for duplicate vehicle identifiers.
|
||||
|
||||
An interview problem may provide this input:
|
||||
|
||||
```text
|
||||
ABC123
|
||||
ABC123
|
||||
ABC123
|
||||
```
|
||||
|
||||
The result is obvious.
|
||||
|
||||
A real system may receive:
|
||||
|
||||
```text
|
||||
ABC123
|
||||
abc123
|
||||
ABC-123
|
||||
ABC123
|
||||
ABC123
|
||||
```
|
||||
|
||||
At the string level, these values are different.
|
||||
|
||||
A duplicate-search algorithm will correctly report that they do not match.
|
||||
|
||||
At the domain level, however, they may represent the same object.
|
||||
|
||||
Before searching for duplicates, the system must define a canonical representation:
|
||||
|
||||
- Is character case significant?
|
||||
- Are separators meaningful?
|
||||
- Should surrounding whitespace be removed?
|
||||
- Which characters are permitted?
|
||||
- Is there a canonical format?
|
||||
- What should happen when the input is ambiguous?
|
||||
|
||||
After normalization, the values may become:
|
||||
|
||||
```text
|
||||
ABC123
|
||||
ABC123
|
||||
ABC123
|
||||
ABC123
|
||||
ABC123
|
||||
```
|
||||
|
||||
Only now is the duplicate-search algorithm solving the correct problem.
|
||||
|
||||
Before normalization, it was comparing representations rather than entities.
|
||||
|
||||
---
|
||||
|
||||
## Normalization Is Part of the System Model
|
||||
|
||||
Normalization can look like little more than string cleanup.
|
||||
|
||||
In reality, it expresses domain rules.
|
||||
|
||||
For example:
|
||||
|
||||
- letter case may be irrelevant for one identifier and essential for another;
|
||||
- two file paths may refer to the same object while remaining different strings;
|
||||
- phone numbers may contain different country prefixes and formatting;
|
||||
- MAC addresses may use different separators;
|
||||
- timestamps may use different time zones;
|
||||
- measurements may use different units;
|
||||
- sensor values may require calibration.
|
||||
|
||||
Normalization does not merely answer:
|
||||
|
||||
> How should this string be modified?
|
||||
|
||||
It answers:
|
||||
|
||||
> What does this system consider to be the same value?
|
||||
|
||||
There is no universal normalization procedure.
|
||||
|
||||
It depends on the protocol, the contract, and the meaning of the data.
|
||||
|
||||
---
|
||||
|
||||
## Not All Well-Formed Data Is Usable
|
||||
|
||||
Successfully parsing a message does not mean that its contents are safe to use.
|
||||
|
||||
Consider these temperatures:
|
||||
|
||||
```text
|
||||
23.7
|
||||
-40.0
|
||||
65535
|
||||
NaN
|
||||
-273.15
|
||||
```
|
||||
|
||||
Every one of these values may be successfully represented as a number.
|
||||
|
||||
Their meanings, however, are very different.
|
||||
|
||||
`23.7` may be a normal measurement.
|
||||
|
||||
`-40.0` may be valid, or it may be the lower limit of the sensor.
|
||||
|
||||
`65535` may represent unavailable data.
|
||||
|
||||
`NaN` may have appeared after an invalid calculation.
|
||||
|
||||
`-273.15` is numerically valid, but for a particular device it almost certainly indicates a problem.
|
||||
|
||||
This reveals several different levels of correctness.
|
||||
|
||||
### Structural Correctness
|
||||
|
||||
Can the message be parsed?
|
||||
|
||||
### Protocol Correctness
|
||||
|
||||
Does it conform to the expected protocol format and version?
|
||||
|
||||
### Numeric Correctness
|
||||
|
||||
Can the value be represented using the required type?
|
||||
|
||||
### Semantic Correctness
|
||||
|
||||
Does the value make sense within the domain?
|
||||
|
||||
Syntactic validity does not guarantee meaningful data.
|
||||
|
||||
---
|
||||
|
||||
## From Raw Data to a Trusted Object
|
||||
|
||||
It is useful to view input handling not as one large validation step, but as a sequence of state transitions.
|
||||
|
||||
```text
|
||||
Raw bytes
|
||||
│
|
||||
▼
|
||||
Framed data
|
||||
│
|
||||
▼
|
||||
Integrity-checked frame
|
||||
│
|
||||
▼
|
||||
Parsed message
|
||||
│
|
||||
▼
|
||||
Normalized values
|
||||
│
|
||||
▼
|
||||
Semantically valid object
|
||||
│
|
||||
▼
|
||||
Trusted domain object
|
||||
│
|
||||
▼
|
||||
Business algorithm
|
||||
```
|
||||
|
||||
At every stage, the system gains stronger guarantees.
|
||||
|
||||
Raw bytes promise almost nothing.
|
||||
|
||||
After framing, the message boundaries are known.
|
||||
|
||||
After integrity checks, there is evidence that the data was not accidentally corrupted.
|
||||
|
||||
After parsing, typed fields exist.
|
||||
|
||||
After normalization, values use a consistent representation.
|
||||
|
||||
After semantic checks, the object is known to be acceptable within the domain.
|
||||
|
||||
Only then can the data be treated as trusted by a particular layer of the system.
|
||||
|
||||
---
|
||||
|
||||
## Trust Must Be Local
|
||||
|
||||
This leads to an important architectural principle:
|
||||
|
||||
> Data is not simply trusted or untrusted.
|
||||
|
||||
It is trusted only relative to a particular contract.
|
||||
|
||||
A transport layer may guarantee that:
|
||||
|
||||
- the complete frame was received;
|
||||
- the checksum matches;
|
||||
- the length is valid.
|
||||
|
||||
It cannot guarantee that a temperature is physically meaningful.
|
||||
|
||||
A parser may guarantee that:
|
||||
|
||||
- message fields were extracted successfully;
|
||||
- their sizes and types match the protocol.
|
||||
|
||||
It cannot determine whether the value is acceptable for a specific device model.
|
||||
|
||||
That responsibility belongs to another layer.
|
||||
|
||||
Each layer checks its own invariants and passes a stronger representation to the next one.
|
||||
|
||||
---
|
||||
|
||||
## Every Layer Earns Trust for the Next One
|
||||
|
||||
A clean object at an algorithm boundary is not a magical property of the data.
|
||||
|
||||
It is the result of fulfilled contracts.
|
||||
|
||||
One layer says:
|
||||
|
||||
> I verified the integrity of the frame.
|
||||
|
||||
The next says:
|
||||
|
||||
> I parsed the message according to a supported protocol version.
|
||||
|
||||
The next says:
|
||||
|
||||
> I converted the values into the system's internal units.
|
||||
|
||||
The next says:
|
||||
|
||||
> I confirmed that the object is valid within this domain.
|
||||
|
||||
Only then may the business logic assume:
|
||||
|
||||
> This is a valid temperature.
|
||||
|
||||
That assumption is not justified because the external world is reliable.
|
||||
|
||||
It is justified because the previous layers did their work.
|
||||
|
||||
---
|
||||
|
||||
## Why Not Validate Everything Everywhere?
|
||||
|
||||
Distrusting input can lead to another bad conclusion:
|
||||
|
||||
> Every function should repeat every validation step.
|
||||
|
||||
That creates different problems:
|
||||
|
||||
- duplicated logic;
|
||||
- contradictory checks;
|
||||
- unclear ownership of responsibilities;
|
||||
- more complex code;
|
||||
- uncertainty about which guarantees already exist.
|
||||
|
||||
A function that accepts raw bytes must not assume that they are safe.
|
||||
|
||||
A function that accepts an object which can only be created after successful verification does not need to repeat the entire process.
|
||||
|
||||
Good architecture does not eliminate trust.
|
||||
|
||||
It makes the origin of trust explicit.
|
||||
|
||||
---
|
||||
|
||||
## Types as Evidence of the Path Already Taken
|
||||
|
||||
One practical way to express this is to use different types for different processing stages.
|
||||
|
||||
Instead of passing the same generic object through the entire system, the stages can be represented explicitly:
|
||||
|
||||
```cpp
|
||||
struct RawFrame;
|
||||
struct VerifiedFrame;
|
||||
struct ParsedTemperatureMessage;
|
||||
struct NormalizedTemperature;
|
||||
```
|
||||
|
||||
The interfaces can then reflect the available guarantees:
|
||||
|
||||
```cpp
|
||||
std::optional<VerifiedFrame>
|
||||
verify_frame(const RawFrame& frame);
|
||||
|
||||
std::optional<ParsedTemperatureMessage>
|
||||
parse_message(const VerifiedFrame& frame);
|
||||
|
||||
std::optional<NormalizedTemperature>
|
||||
normalize_temperature(const ParsedTemperatureMessage& message);
|
||||
```
|
||||
|
||||
Business logic can accept only the normalized value:
|
||||
|
||||
```cpp
|
||||
void process_temperature(const NormalizedTemperature& temperature);
|
||||
```
|
||||
|
||||
This does not make the data absolutely true.
|
||||
|
||||
It makes the stages already completed explicit.
|
||||
|
||||
It also prevents raw input from being passed accidentally into code that expects a verified object.
|
||||
|
||||
---
|
||||
|
||||
## What Happens When Processing Fails?
|
||||
|
||||
Data evolution does not always end with a valid business object.
|
||||
|
||||
Every stage may reject the input:
|
||||
|
||||
```text
|
||||
Raw bytes
|
||||
│
|
||||
├── incomplete frame
|
||||
├── unsupported version
|
||||
├── invalid checksum
|
||||
├── malformed payload
|
||||
├── unknown sensor
|
||||
├── invalid scaling
|
||||
├── out-of-range value
|
||||
└── valid temperature
|
||||
```
|
||||
|
||||
This introduces another major part of real engineering that is usually absent from algorithmic problems:
|
||||
|
||||
- the message may need to be discarded;
|
||||
- the failure may need to be logged;
|
||||
- a diagnostic counter may need to be incremented;
|
||||
- the source may need to be reconnected;
|
||||
- the system may need to use the last known valid value;
|
||||
- a component may enter a degraded mode;
|
||||
- the failure may affect safety-related behavior.
|
||||
|
||||
In an interview problem, an invalid value is often just an edge case.
|
||||
|
||||
In a real system, it may trigger an entirely different operating scenario.
|
||||
|
||||
---
|
||||
|
||||
## The Algorithm Still Matters
|
||||
|
||||
None of this means that algorithms are unimportant.
|
||||
|
||||
Once data has been converted into a correct internal model, the algorithm still needs to be:
|
||||
|
||||
- correct;
|
||||
- efficient;
|
||||
- understandable;
|
||||
- appropriate for the system constraints.
|
||||
|
||||
The problem begins when solving a task over a clean array is treated as a complete model of engineering ability.
|
||||
|
||||
An algorithm solves a problem under a set of assumptions.
|
||||
|
||||
An engineer must also:
|
||||
|
||||
- discover those assumptions;
|
||||
- determine whether they are valid;
|
||||
- assign responsibility for enforcing them;
|
||||
- express the resulting guarantees in interfaces and architecture.
|
||||
|
||||
---
|
||||
|
||||
## What This Actually Tests
|
||||
|
||||
A problem over a ready-made container can test:
|
||||
|
||||
- knowledge of data structures;
|
||||
- algorithmic reasoning;
|
||||
- complexity analysis;
|
||||
- recognition of known patterns;
|
||||
- implementation accuracy.
|
||||
|
||||
It says much less about a candidate's ability to:
|
||||
|
||||
- work with external data sources;
|
||||
- design trust boundaries;
|
||||
- parse protocols;
|
||||
- normalize representations;
|
||||
- define semantic validity;
|
||||
- design diagnostics;
|
||||
- handle partial failures;
|
||||
- create reliable contracts between layers.
|
||||
|
||||
This does not make the algorithmic task useless.
|
||||
|
||||
It only limits what can reasonably be concluded from it.
|
||||
|
||||
---
|
||||
|
||||
## Where the Interview Ends and Engineering Begins
|
||||
|
||||
An interview problem often presents this model:
|
||||
|
||||
```text
|
||||
Clean input
|
||||
│
|
||||
▼
|
||||
Algorithm
|
||||
│
|
||||
▼
|
||||
Result
|
||||
```
|
||||
|
||||
A real system often looks more like this:
|
||||
|
||||
```text
|
||||
Physical world
|
||||
│
|
||||
▼
|
||||
Electrical signal
|
||||
│
|
||||
▼
|
||||
Raw bytes
|
||||
│
|
||||
▼
|
||||
Transport framing
|
||||
│
|
||||
▼
|
||||
Integrity checks
|
||||
│
|
||||
▼
|
||||
Protocol parsing
|
||||
│
|
||||
▼
|
||||
Version handling
|
||||
│
|
||||
▼
|
||||
Normalization
|
||||
│
|
||||
▼
|
||||
Semantic validation
|
||||
│
|
||||
▼
|
||||
Domain object
|
||||
│
|
||||
▼
|
||||
Algorithm
|
||||
│
|
||||
▼
|
||||
System decision
|
||||
```
|
||||
|
||||
The interview begins near the end of this chain.
|
||||
|
||||
Engineering is responsible for the entire chain.
|
||||
|
||||
---
|
||||
|
||||
## The Evolution of Data
|
||||
|
||||
We can now return to the temperature example.
|
||||
|
||||
Initially, the system does not have a temperature.
|
||||
|
||||
It has a signal.
|
||||
|
||||
Then it has bytes.
|
||||
|
||||
Then a frame.
|
||||
|
||||
Then a message.
|
||||
|
||||
Then a raw sensor value.
|
||||
|
||||
Then a value expressed in physical units.
|
||||
|
||||
Then a normalized and semantically valid measurement.
|
||||
|
||||
Only after all of that does a number appear that can safely be stored in a container and passed to an algorithm.
|
||||
|
||||
```text
|
||||
Signal
|
||||
↓
|
||||
Bytes
|
||||
↓
|
||||
Frame
|
||||
↓
|
||||
Verified frame
|
||||
↓
|
||||
Parsed message
|
||||
↓
|
||||
Raw sensor value
|
||||
↓
|
||||
Calibrated value
|
||||
↓
|
||||
Normalized temperature
|
||||
↓
|
||||
Trusted domain object
|
||||
↓
|
||||
std::vector<float>
|
||||
↓
|
||||
std::max_element
|
||||
```
|
||||
|
||||
The maximum-search algorithm does not create the meaning of the data.
|
||||
|
||||
It consumes meaning that was established by the previous layers.
|
||||
|
||||
---
|
||||
|
||||
## Key Takeaway
|
||||
|
||||
Clean input is not a starting point.
|
||||
|
||||
It is an engineering result.
|
||||
|
||||
It exists only after the system has:
|
||||
|
||||
- identified the structure of the data;
|
||||
- verified its integrity;
|
||||
- understood its format;
|
||||
- converted it into a canonical representation;
|
||||
- checked its meaning;
|
||||
- established a contract of trust.
|
||||
|
||||
The engineer's first question is therefore not:
|
||||
|
||||
> How do I process this array?
|
||||
|
||||
It is:
|
||||
|
||||
> Why can this array be trusted?
|
||||
|
||||
And then:
|
||||
|
||||
> Which layer guarantees that?
|
||||
|
||||
---
|
||||
|
||||
## Project Perspective
|
||||
|
||||
> Exists in real engineering?
|
||||
> Yes. Almost constantly.
|
||||
|
||||
> Exists in interview form?
|
||||
> Usually not. Most of the data journey is hidden by the problem statement.
|
||||
|
||||
Algorithmic tasks are useful for evaluating work on already prepared structures.
|
||||
|
||||
But they usually begin with a result that a real system still has to produce.
|
||||
|
||||
That is the myth of clean input:
|
||||
|
||||
> Data does not arrive ready for the algorithm.
|
||||
|
||||
> Engineering makes it ready.
|
||||
Reference in New Issue
Block a user